@posthog/agent 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +296 -0
- package/README.md +142 -0
- package/dist/example.d.ts +3 -0
- package/dist/example.d.ts.map +1 -0
- package/dist/example.js +49 -0
- package/dist/example.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/src/agent-registry.d.ts +16 -0
- package/dist/src/agent-registry.d.ts.map +1 -0
- package/dist/src/agent-registry.js +54 -0
- package/dist/src/agent-registry.js.map +1 -0
- package/dist/src/agent.d.ts +60 -0
- package/dist/src/agent.d.ts.map +1 -0
- package/dist/src/agent.js +371 -0
- package/dist/src/agent.js.map +1 -0
- package/dist/src/agents/execution.d.ts +2 -0
- package/dist/src/agents/execution.d.ts.map +1 -0
- package/dist/src/agents/execution.js +54 -0
- package/dist/src/agents/execution.js.map +1 -0
- package/dist/src/agents/planning.d.ts +2 -0
- package/dist/src/agents/planning.d.ts.map +1 -0
- package/dist/src/agents/planning.js +68 -0
- package/dist/src/agents/planning.js.map +1 -0
- package/dist/src/event-transformer.d.ts +7 -0
- package/dist/src/event-transformer.d.ts.map +1 -0
- package/dist/src/event-transformer.js +174 -0
- package/dist/src/event-transformer.js.map +1 -0
- package/dist/src/file-manager.d.ts +30 -0
- package/dist/src/file-manager.d.ts.map +1 -0
- package/dist/src/file-manager.js +181 -0
- package/dist/src/file-manager.js.map +1 -0
- package/dist/src/git-manager.d.ts +49 -0
- package/dist/src/git-manager.d.ts.map +1 -0
- package/dist/src/git-manager.js +278 -0
- package/dist/src/git-manager.js.map +1 -0
- package/dist/src/posthog-api.d.ts +48 -0
- package/dist/src/posthog-api.d.ts.map +1 -0
- package/dist/src/posthog-api.js +110 -0
- package/dist/src/posthog-api.js.map +1 -0
- package/dist/src/prompt-builder.d.ts +17 -0
- package/dist/src/prompt-builder.d.ts.map +1 -0
- package/dist/src/prompt-builder.js +75 -0
- package/dist/src/prompt-builder.js.map +1 -0
- package/dist/src/stage-executor.d.ts +16 -0
- package/dist/src/stage-executor.d.ts.map +1 -0
- package/dist/src/stage-executor.js +119 -0
- package/dist/src/stage-executor.js.map +1 -0
- package/dist/src/task-manager.d.ts +25 -0
- package/dist/src/task-manager.d.ts.map +1 -0
- package/dist/src/task-manager.js +119 -0
- package/dist/src/task-manager.js.map +1 -0
- package/dist/src/template-manager.d.ts +30 -0
- package/dist/src/template-manager.d.ts.map +1 -0
- package/dist/src/template-manager.js +118 -0
- package/dist/src/template-manager.js.map +1 -0
- package/dist/src/types.d.ts +163 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +9 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/logger.d.ts +29 -0
- package/dist/src/utils/logger.d.ts.map +1 -0
- package/dist/src/utils/logger.js +66 -0
- package/dist/src/utils/logger.js.map +1 -0
- package/dist/src/utils/mcp.d.ts +10 -0
- package/dist/src/utils/mcp.d.ts.map +1 -0
- package/dist/src/utils/mcp.js +16 -0
- package/dist/src/utils/mcp.js.map +1 -0
- package/dist/src/workflow-registry.d.ts +11 -0
- package/dist/src/workflow-registry.d.ts.map +1 -0
- package/dist/src/workflow-registry.js +26 -0
- package/dist/src/workflow-registry.js.map +1 -0
- package/dist/src/workflow-types.d.ts +45 -0
- package/dist/src/workflow-types.d.ts.map +1 -0
- package/dist/src/workflow-types.js +2 -0
- package/dist/src/workflow-types.js.map +1 -0
- package/package.json +61 -0
- package/src/agent-registry.ts +60 -0
- package/src/agent.ts +428 -0
- package/src/agents/execution.ts +53 -0
- package/src/agents/planning.ts +67 -0
- package/src/event-transformer.ts +189 -0
- package/src/file-manager.ts +204 -0
- package/src/git-manager.ts +344 -0
- package/src/posthog-api.ts +169 -0
- package/src/prompt-builder.ts +93 -0
- package/src/stage-executor.ts +137 -0
- package/src/task-manager.ts +155 -0
- package/src/template-manager.ts +149 -0
- package/src/templates/plan-template.md +45 -0
- package/src/types.ts +223 -0
- package/src/utils/logger.ts +79 -0
- package/src/utils/mcp.ts +15 -0
- package/src/workflow-registry.ts +31 -0
- package/src/workflow-types.ts +53 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import type { AgentEvent } from './types.js';
|
|
2
|
+
import type { SDKMessage } from '@anthropic-ai/claude-agent-sdk';
|
|
3
|
+
|
|
4
|
+
export class EventTransformer {
|
|
5
|
+
transform(sdkMessage: SDKMessage): AgentEvent | null {
|
|
6
|
+
const baseEvent = { ts: Date.now() };
|
|
7
|
+
|
|
8
|
+
// Handle stream events
|
|
9
|
+
if (sdkMessage.type === 'stream_event') {
|
|
10
|
+
const event = sdkMessage.event;
|
|
11
|
+
|
|
12
|
+
switch (event.type) {
|
|
13
|
+
case 'message_start':
|
|
14
|
+
return {
|
|
15
|
+
...baseEvent,
|
|
16
|
+
type: 'message_start',
|
|
17
|
+
messageId: event.message?.id,
|
|
18
|
+
model: event.message?.model
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
case 'content_block_start':
|
|
22
|
+
const contentBlock = event.content_block;
|
|
23
|
+
if (!contentBlock) return null;
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
...baseEvent,
|
|
27
|
+
type: 'content_block_start',
|
|
28
|
+
index: event.index,
|
|
29
|
+
contentType: contentBlock.type as 'text' | 'tool_use' | 'thinking',
|
|
30
|
+
toolName: contentBlock.type === 'tool_use' ? contentBlock.name : undefined,
|
|
31
|
+
toolId: contentBlock.type === 'tool_use' ? contentBlock.id : undefined
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
case 'content_block_delta':
|
|
35
|
+
const delta = event.delta;
|
|
36
|
+
if (!delta) return null;
|
|
37
|
+
|
|
38
|
+
if (delta.type === 'text_delta') {
|
|
39
|
+
return {
|
|
40
|
+
...baseEvent,
|
|
41
|
+
type: 'token',
|
|
42
|
+
content: delta.text,
|
|
43
|
+
contentType: 'text'
|
|
44
|
+
};
|
|
45
|
+
} else if (delta.type === 'input_json_delta') {
|
|
46
|
+
return {
|
|
47
|
+
...baseEvent,
|
|
48
|
+
type: 'token',
|
|
49
|
+
content: delta.partial_json,
|
|
50
|
+
contentType: 'tool_input'
|
|
51
|
+
};
|
|
52
|
+
} else if (delta.type === 'thinking_delta') {
|
|
53
|
+
return {
|
|
54
|
+
...baseEvent,
|
|
55
|
+
type: 'token',
|
|
56
|
+
content: delta.thinking,
|
|
57
|
+
contentType: 'thinking'
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
|
|
62
|
+
case 'content_block_stop':
|
|
63
|
+
return {
|
|
64
|
+
...baseEvent,
|
|
65
|
+
type: 'content_block_stop',
|
|
66
|
+
index: event.index
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
case 'message_delta':
|
|
70
|
+
return {
|
|
71
|
+
...baseEvent,
|
|
72
|
+
type: 'message_delta',
|
|
73
|
+
stopReason: event.delta?.stop_reason,
|
|
74
|
+
stopSequence: event.delta?.stop_sequence,
|
|
75
|
+
usage: event.usage ? {
|
|
76
|
+
outputTokens: event.usage.output_tokens
|
|
77
|
+
} : undefined
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
case 'message_stop':
|
|
81
|
+
return {
|
|
82
|
+
...baseEvent,
|
|
83
|
+
type: 'message_stop'
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
case 'ping':
|
|
87
|
+
// Ignore ping events
|
|
88
|
+
return null;
|
|
89
|
+
|
|
90
|
+
case 'error':
|
|
91
|
+
return {
|
|
92
|
+
...baseEvent,
|
|
93
|
+
type: 'error',
|
|
94
|
+
message: event.error?.message || 'Unknown error',
|
|
95
|
+
error: event.error,
|
|
96
|
+
errorType: event.error?.type
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
default:
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Handle assistant messages (full message, not streaming)
|
|
105
|
+
if (sdkMessage.type === 'assistant') {
|
|
106
|
+
// Extract tool calls from assistant message
|
|
107
|
+
const message = sdkMessage.message;
|
|
108
|
+
// We could emit individual tool_call events here if needed
|
|
109
|
+
// For now, just emit a status event
|
|
110
|
+
return {
|
|
111
|
+
...baseEvent,
|
|
112
|
+
type: 'status',
|
|
113
|
+
phase: 'assistant_message',
|
|
114
|
+
messageId: message.id,
|
|
115
|
+
model: message.model
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Handle user messages
|
|
120
|
+
if (sdkMessage.type === 'user') {
|
|
121
|
+
const content = sdkMessage.message.content;
|
|
122
|
+
const textContent = Array.isArray(content)
|
|
123
|
+
? content.find(c => c.type === 'text')?.text
|
|
124
|
+
: typeof content === 'string' ? content : '';
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
...baseEvent,
|
|
128
|
+
type: 'user_message',
|
|
129
|
+
content: textContent || '',
|
|
130
|
+
isSynthetic: sdkMessage.isSynthetic
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Handle result messages
|
|
135
|
+
if (sdkMessage.type === 'result') {
|
|
136
|
+
if (sdkMessage.subtype === 'success') {
|
|
137
|
+
return {
|
|
138
|
+
...baseEvent,
|
|
139
|
+
type: 'done',
|
|
140
|
+
durationMs: sdkMessage.duration_ms,
|
|
141
|
+
numTurns: sdkMessage.num_turns,
|
|
142
|
+
totalCostUsd: sdkMessage.total_cost_usd,
|
|
143
|
+
usage: sdkMessage.usage
|
|
144
|
+
};
|
|
145
|
+
} else {
|
|
146
|
+
return {
|
|
147
|
+
...baseEvent,
|
|
148
|
+
type: 'error',
|
|
149
|
+
message: `Execution failed: ${sdkMessage.subtype}`,
|
|
150
|
+
error: { subtype: sdkMessage.subtype },
|
|
151
|
+
errorType: sdkMessage.subtype
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Handle system messages
|
|
157
|
+
if (sdkMessage.type === 'system') {
|
|
158
|
+
if (sdkMessage.subtype === 'init') {
|
|
159
|
+
return {
|
|
160
|
+
...baseEvent,
|
|
161
|
+
type: 'init',
|
|
162
|
+
model: sdkMessage.model,
|
|
163
|
+
tools: sdkMessage.tools,
|
|
164
|
+
permissionMode: sdkMessage.permissionMode,
|
|
165
|
+
cwd: sdkMessage.cwd,
|
|
166
|
+
apiKeySource: sdkMessage.apiKeySource
|
|
167
|
+
};
|
|
168
|
+
} else if (sdkMessage.subtype === 'compact_boundary') {
|
|
169
|
+
return {
|
|
170
|
+
...baseEvent,
|
|
171
|
+
type: 'compact_boundary',
|
|
172
|
+
trigger: sdkMessage.compact_metadata.trigger,
|
|
173
|
+
preTokens: sdkMessage.compact_metadata.pre_tokens
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
createStatusEvent(phase: string, additionalData?: any): AgentEvent {
|
|
182
|
+
return {
|
|
183
|
+
type: 'status',
|
|
184
|
+
ts: Date.now(),
|
|
185
|
+
phase,
|
|
186
|
+
...additionalData
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
import type { SupportingFile } from './types';
|
|
4
|
+
import { Logger } from './utils/logger';
|
|
5
|
+
|
|
6
|
+
export interface TaskFile {
|
|
7
|
+
name: string;
|
|
8
|
+
content: string;
|
|
9
|
+
type: 'plan' | 'context' | 'reference' | 'output' | 'artifact';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class PostHogFileManager {
|
|
13
|
+
private repositoryPath: string;
|
|
14
|
+
private logger: Logger;
|
|
15
|
+
|
|
16
|
+
constructor(repositoryPath: string, logger?: Logger) {
|
|
17
|
+
this.repositoryPath = repositoryPath;
|
|
18
|
+
this.logger = logger || new Logger({ debug: false, prefix: '[FileManager]' });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private getTaskDirectory(taskId: string): string {
|
|
22
|
+
return join(this.repositoryPath, '.posthog', taskId);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private getTaskFilePath(taskId: string, fileName: string): string {
|
|
26
|
+
return join(this.getTaskDirectory(taskId), fileName);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async ensureTaskDirectory(taskId: string): Promise<void> {
|
|
30
|
+
const taskDir = this.getTaskDirectory(taskId);
|
|
31
|
+
try {
|
|
32
|
+
await fs.access(taskDir);
|
|
33
|
+
} catch {
|
|
34
|
+
await fs.mkdir(taskDir, { recursive: true });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async writeTaskFile(taskId: string, file: TaskFile): Promise<void> {
|
|
39
|
+
await this.ensureTaskDirectory(taskId);
|
|
40
|
+
const filePath = this.getTaskFilePath(taskId, file.name);
|
|
41
|
+
|
|
42
|
+
this.logger.debug('Writing task file', {
|
|
43
|
+
filePath,
|
|
44
|
+
contentLength: file.content.length,
|
|
45
|
+
contentType: typeof file.content
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
await fs.writeFile(filePath, file.content, 'utf8');
|
|
49
|
+
|
|
50
|
+
this.logger.debug('File written successfully', { filePath });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async readTaskFile(taskId: string, fileName: string): Promise<string | null> {
|
|
54
|
+
try {
|
|
55
|
+
const filePath = this.getTaskFilePath(taskId, fileName);
|
|
56
|
+
return await fs.readFile(filePath, 'utf8');
|
|
57
|
+
} catch (error) {
|
|
58
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async listTaskFiles(taskId: string): Promise<string[]> {
|
|
66
|
+
try {
|
|
67
|
+
const taskDir = this.getTaskDirectory(taskId);
|
|
68
|
+
const files = await fs.readdir(taskDir);
|
|
69
|
+
return files.filter(file => !file.startsWith('.'));
|
|
70
|
+
} catch (error) {
|
|
71
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async deleteTaskFile(taskId: string, fileName: string): Promise<void> {
|
|
79
|
+
try {
|
|
80
|
+
const filePath = this.getTaskFilePath(taskId, fileName);
|
|
81
|
+
await fs.unlink(filePath);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async taskDirectoryExists(taskId: string): Promise<boolean> {
|
|
90
|
+
try {
|
|
91
|
+
const taskDir = this.getTaskDirectory(taskId);
|
|
92
|
+
await fs.access(taskDir);
|
|
93
|
+
return true;
|
|
94
|
+
} catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async cleanupTaskDirectory(taskId: string): Promise<void> {
|
|
100
|
+
try {
|
|
101
|
+
const taskDir = this.getTaskDirectory(taskId);
|
|
102
|
+
await fs.rm(taskDir, { recursive: true, force: true });
|
|
103
|
+
} catch (error) {
|
|
104
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Convenience methods for common file types
|
|
111
|
+
async writePlan(taskId: string, plan: string): Promise<void> {
|
|
112
|
+
this.logger.debug('Writing plan', {
|
|
113
|
+
taskId,
|
|
114
|
+
planLength: plan.length,
|
|
115
|
+
contentPreview: plan.substring(0, 200)
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
await this.writeTaskFile(taskId, {
|
|
119
|
+
name: 'plan.md',
|
|
120
|
+
content: plan,
|
|
121
|
+
type: 'plan'
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
this.logger.info('Plan file written', { taskId });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async readPlan(taskId: string): Promise<string | null> {
|
|
128
|
+
return await this.readTaskFile(taskId, 'plan.md');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async writeContext(taskId: string, context: string): Promise<void> {
|
|
132
|
+
await this.writeTaskFile(taskId, {
|
|
133
|
+
name: 'context.md',
|
|
134
|
+
content: context,
|
|
135
|
+
type: 'context'
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async readContext(taskId: string): Promise<string | null> {
|
|
140
|
+
return await this.readTaskFile(taskId, 'context.md');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async writeRequirements(taskId: string, requirements: string): Promise<void> {
|
|
144
|
+
await this.writeTaskFile(taskId, {
|
|
145
|
+
name: 'requirements.md',
|
|
146
|
+
content: requirements,
|
|
147
|
+
type: 'reference'
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async readRequirements(taskId: string): Promise<string | null> {
|
|
152
|
+
return await this.readTaskFile(taskId, 'requirements.md');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async getTaskFiles(taskId: string): Promise<SupportingFile[]> {
|
|
156
|
+
const fileNames = await this.listTaskFiles(taskId);
|
|
157
|
+
const files: SupportingFile[] = [];
|
|
158
|
+
|
|
159
|
+
for (const fileName of fileNames) {
|
|
160
|
+
const content = await this.readTaskFile(taskId, fileName);
|
|
161
|
+
if (content !== null) {
|
|
162
|
+
// Determine type based on file name
|
|
163
|
+
let type: SupportingFile['type'] = 'reference';
|
|
164
|
+
if (fileName === 'plan.md') type = 'plan';
|
|
165
|
+
else if (fileName === 'context.md') type = 'context';
|
|
166
|
+
else if (fileName === 'requirements.md') type = 'reference';
|
|
167
|
+
else if (fileName.startsWith('output_')) type = 'output';
|
|
168
|
+
|
|
169
|
+
files.push({
|
|
170
|
+
name: fileName,
|
|
171
|
+
content,
|
|
172
|
+
type,
|
|
173
|
+
created_at: new Date().toISOString() // Could be enhanced with file stats
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return files;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async ensureGitignore(): Promise<void> {
|
|
182
|
+
const gitignorePath = join(this.repositoryPath, '.posthog', '.gitignore');
|
|
183
|
+
const gitignoreContent = `# PostHog task artifacts - customize as needed
|
|
184
|
+
# Exclude temporary files
|
|
185
|
+
*/temp/
|
|
186
|
+
*/cache/
|
|
187
|
+
*/.env
|
|
188
|
+
*/.secrets
|
|
189
|
+
|
|
190
|
+
# Include plans and documentation by default
|
|
191
|
+
!*/plan.md
|
|
192
|
+
!*/context.md
|
|
193
|
+
!*/requirements.md
|
|
194
|
+
!*/README.md
|
|
195
|
+
`;
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
await fs.access(gitignorePath);
|
|
199
|
+
} catch {
|
|
200
|
+
await fs.mkdir(dirname(gitignorePath), { recursive: true });
|
|
201
|
+
await fs.writeFile(gitignorePath, gitignoreContent, 'utf8');
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|