@posthog/agent 1.19.0 → 1.21.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.
Files changed (72) hide show
  1. package/dist/claude-cli/cli.js +3197 -2675
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +1 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/src/adapters/claude/claude-adapter.d.ts +4 -3
  7. package/dist/src/adapters/claude/claude-adapter.d.ts.map +1 -1
  8. package/dist/src/adapters/claude/claude-adapter.js +149 -133
  9. package/dist/src/adapters/claude/claude-adapter.js.map +1 -1
  10. package/dist/src/adapters/types.d.ts +9 -4
  11. package/dist/src/adapters/types.d.ts.map +1 -1
  12. package/dist/src/agent.d.ts +1 -1
  13. package/dist/src/agent.d.ts.map +1 -1
  14. package/dist/src/agent.js +9 -8
  15. package/dist/src/agent.js.map +1 -1
  16. package/dist/src/agents/research.d.ts +1 -1
  17. package/dist/src/agents/research.d.ts.map +1 -1
  18. package/dist/src/agents/research.js +55 -5
  19. package/dist/src/agents/research.js.map +1 -1
  20. package/dist/src/file-manager.d.ts +12 -0
  21. package/dist/src/file-manager.d.ts.map +1 -1
  22. package/dist/src/file-manager.js +76 -10
  23. package/dist/src/file-manager.js.map +1 -1
  24. package/dist/src/posthog-api.d.ts +2 -1
  25. package/dist/src/posthog-api.d.ts.map +1 -1
  26. package/dist/src/posthog-api.js +11 -0
  27. package/dist/src/posthog-api.js.map +1 -1
  28. package/dist/src/prompt-builder.d.ts.map +1 -1
  29. package/dist/src/prompt-builder.js +25 -0
  30. package/dist/src/prompt-builder.js.map +1 -1
  31. package/dist/src/task-progress-reporter.d.ts +12 -4
  32. package/dist/src/task-progress-reporter.d.ts.map +1 -1
  33. package/dist/src/task-progress-reporter.js +271 -117
  34. package/dist/src/task-progress-reporter.js.map +1 -1
  35. package/dist/src/todo-manager.d.ts +29 -0
  36. package/dist/src/todo-manager.d.ts.map +1 -0
  37. package/dist/src/todo-manager.js +126 -0
  38. package/dist/src/todo-manager.js.map +1 -0
  39. package/dist/src/types.d.ts +17 -1
  40. package/dist/src/types.d.ts.map +1 -1
  41. package/dist/src/types.js.map +1 -1
  42. package/dist/src/workflow/config.d.ts.map +1 -1
  43. package/dist/src/workflow/config.js +11 -0
  44. package/dist/src/workflow/config.js.map +1 -1
  45. package/dist/src/workflow/steps/build.d.ts.map +1 -1
  46. package/dist/src/workflow/steps/build.js +10 -3
  47. package/dist/src/workflow/steps/build.js.map +1 -1
  48. package/dist/src/workflow/steps/finalize.d.ts +3 -0
  49. package/dist/src/workflow/steps/finalize.d.ts.map +1 -0
  50. package/dist/src/workflow/steps/finalize.js +173 -0
  51. package/dist/src/workflow/steps/finalize.js.map +1 -0
  52. package/dist/src/workflow/steps/plan.d.ts.map +1 -1
  53. package/dist/src/workflow/steps/plan.js +13 -6
  54. package/dist/src/workflow/steps/plan.js.map +1 -1
  55. package/dist/src/workflow/steps/research.js +3 -3
  56. package/dist/src/workflow/steps/research.js.map +1 -1
  57. package/package.json +1 -1
  58. package/src/adapters/claude/claude-adapter.ts +67 -48
  59. package/src/adapters/types.ts +10 -4
  60. package/src/agent.ts +17 -8
  61. package/src/agents/research.ts +55 -5
  62. package/src/file-manager.ts +89 -6
  63. package/src/posthog-api.ts +33 -1
  64. package/src/prompt-builder.ts +24 -0
  65. package/src/task-progress-reporter.ts +299 -138
  66. package/src/todo-manager.ts +169 -0
  67. package/src/types.ts +20 -1
  68. package/src/workflow/config.ts +11 -0
  69. package/src/workflow/steps/build.ts +12 -3
  70. package/src/workflow/steps/finalize.ts +207 -0
  71. package/src/workflow/steps/plan.ts +16 -6
  72. package/src/workflow/steps/research.ts +3 -3
@@ -1,6 +1,7 @@
1
1
  import { query } from '@anthropic-ai/claude-agent-sdk';
2
2
  import { PLANNING_SYSTEM_PROMPT } from '../../agents/planning.js';
3
3
  import { finalizeStepGitActions } from '../utils.js';
4
+ import { TodoManager } from '../../todo-manager.js';
4
5
 
5
6
  const planStep = async ({ step, context }) => {
6
7
  const { task, cwd, isCloudMode, options, logger, fileManager, gitManager, promptBuilder, adapter, mcpServers, emitEvent, } = context;
@@ -72,17 +73,23 @@ const planStep = async ({ step, context }) => {
72
73
  prompt: fullPrompt,
73
74
  options: { ...baseOptions, ...(options.queryOverrides || {}) },
74
75
  });
76
+ const todoManager = new TodoManager(fileManager, stepLogger);
75
77
  let planContent = '';
76
78
  for await (const message of response) {
77
79
  emitEvent(adapter.createRawSDKEvent(message));
78
- const transformed = adapter.transform(message);
79
- if (transformed) {
80
- emitEvent(transformed);
80
+ const transformedEvents = adapter.transform(message);
81
+ for (const event of transformedEvents) {
82
+ emitEvent(event);
81
83
  }
84
+ const todoList = await todoManager.checkAndPersistFromMessage(message, task.id);
85
+ if (todoList) {
86
+ emitEvent(adapter.createArtifactEvent('todos', todoList));
87
+ }
88
+ // Extract text content for plan
82
89
  if (message.type === 'assistant' && message.message?.content) {
83
- for (const c of message.message.content) {
84
- if (c.type === 'text' && c.text) {
85
- planContent += `${c.text}\n`;
90
+ for (const block of message.message.content) {
91
+ if (block.type === 'text' && block.text) {
92
+ planContent += `${block.text}\n`;
86
93
  }
87
94
  }
88
95
  }
@@ -1 +1 @@
1
- {"version":3,"file":"plan.js","sources":["../../../../src/workflow/steps/plan.ts"],"sourcesContent":["import { query } from '@anthropic-ai/claude-agent-sdk';\nimport { PLANNING_SYSTEM_PROMPT } from '../../agents/planning.js';\nimport type { WorkflowStepRunner } from '../types.js';\nimport { finalizeStepGitActions } from '../utils.js';\n\nexport const planStep: 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('PlanStep');\n\n const existingPlan = await fileManager.readPlan(task.id);\n if (existingPlan) {\n stepLogger.info('Plan already exists, skipping step', { taskId: task.id });\n return { status: 'skipped' };\n }\n\n const researchData = await fileManager.readResearch(task.id);\n if (researchData?.questions && !researchData.answered) {\n stepLogger.info('Waiting for answered research questions', { taskId: task.id });\n emitEvent(adapter.createStatusEvent('phase_complete', { phase: 'research_questions' }));\n return { status: 'skipped', halt: true };\n }\n\n stepLogger.info('Starting planning phase', { taskId: task.id });\n emitEvent(adapter.createStatusEvent('phase_start', { phase: 'planning' }));\n let researchContext = '';\n if (researchData) {\n researchContext += `## Research Context\\n\\n${researchData.context}\\n\\n`;\n if (researchData.keyFiles.length > 0) {\n researchContext += `**Key Files:**\\n${researchData.keyFiles.map(f => `- ${f}`).join('\\n')}\\n\\n`;\n }\n if (researchData.blockers && researchData.blockers.length > 0) {\n researchContext += `**Considerations:**\\n${researchData.blockers.map(b => `- ${b}`).join('\\n')}\\n\\n`;\n }\n\n // Add answered questions if they exist\n if (researchData.questions && researchData.answers && researchData.answered) {\n researchContext += `## Implementation Decisions\\n\\n`;\n for (const question of researchData.questions) {\n const answer = researchData.answers.find(\n (a) => a.questionId === question.id\n );\n\n researchContext += `### ${question.question}\\n\\n`;\n if (answer) {\n researchContext += `**Selected:** ${answer.selectedOption}\\n`;\n if (answer.customInput) {\n researchContext += `**Details:** ${answer.customInput}\\n`;\n }\n } else {\n researchContext += `**Selected:** Not answered\\n`;\n }\n researchContext += `\\n`;\n }\n }\n }\n\n const planningPrompt = await promptBuilder.buildPlanningPrompt(task, cwd);\n const fullPrompt = `${PLANNING_SYSTEM_PROMPT}\\n\\n${planningPrompt}\\n\\n${researchContext}`;\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, MCP resources, and ExitPlanMode\n allowedTools: [\n 'Read',\n 'Glob',\n 'Grep',\n 'WebFetch',\n 'WebSearch',\n 'ListMcpResources',\n 'ReadMcpResource',\n 'ExitPlanMode',\n 'TodoWrite',\n 'BashOutput',\n ],\n };\n\n const response = query({\n prompt: fullPrompt,\n options: { ...baseOptions, ...(options.queryOverrides || {}) },\n });\n\n let planContent = '';\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 planContent += `${c.text}\\n`;\n }\n }\n }\n }\n\n if (planContent.trim()) {\n await fileManager.writePlan(task.id, planContent.trim());\n stepLogger.info('Plan completed', { taskId: task.id });\n }\n\n await gitManager.addAllPostHogFiles();\n await finalizeStepGitActions(context, step, {\n commitMessage: `Planning phase for ${task.title}`,\n });\n\n if (!isCloudMode) {\n emitEvent(adapter.createStatusEvent('phase_complete', { phase: 'planning' }));\n return { status: 'completed', halt: true };\n }\n\n emitEvent(adapter.createStatusEvent('phase_complete', { phase: 'planning' }));\n return { status: 'completed' };\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"plan.js","sources":["../../../../src/workflow/steps/plan.ts"],"sourcesContent":["import { query } from '@anthropic-ai/claude-agent-sdk';\nimport { PLANNING_SYSTEM_PROMPT } from '../../agents/planning.js';\nimport type { WorkflowStepRunner } from '../types.js';\nimport { finalizeStepGitActions } from '../utils.js';\nimport { TodoManager } from '../../todo-manager.js';\n\nexport const planStep: 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('PlanStep');\n\n const existingPlan = await fileManager.readPlan(task.id);\n if (existingPlan) {\n stepLogger.info('Plan already exists, skipping step', { taskId: task.id });\n return { status: 'skipped' };\n }\n\n const researchData = await fileManager.readResearch(task.id);\n if (researchData?.questions && !researchData.answered) {\n stepLogger.info('Waiting for answered research questions', { taskId: task.id });\n emitEvent(adapter.createStatusEvent('phase_complete', { phase: 'research_questions' }));\n return { status: 'skipped', halt: true };\n }\n\n stepLogger.info('Starting planning phase', { taskId: task.id });\n emitEvent(adapter.createStatusEvent('phase_start', { phase: 'planning' }));\n let researchContext = '';\n if (researchData) {\n researchContext += `## Research Context\\n\\n${researchData.context}\\n\\n`;\n if (researchData.keyFiles.length > 0) {\n researchContext += `**Key Files:**\\n${researchData.keyFiles.map(f => `- ${f}`).join('\\n')}\\n\\n`;\n }\n if (researchData.blockers && researchData.blockers.length > 0) {\n researchContext += `**Considerations:**\\n${researchData.blockers.map(b => `- ${b}`).join('\\n')}\\n\\n`;\n }\n\n // Add answered questions if they exist\n if (researchData.questions && researchData.answers && researchData.answered) {\n researchContext += `## Implementation Decisions\\n\\n`;\n for (const question of researchData.questions) {\n const answer = researchData.answers.find(\n (a) => a.questionId === question.id\n );\n\n researchContext += `### ${question.question}\\n\\n`;\n if (answer) {\n researchContext += `**Selected:** ${answer.selectedOption}\\n`;\n if (answer.customInput) {\n researchContext += `**Details:** ${answer.customInput}\\n`;\n }\n } else {\n researchContext += `**Selected:** Not answered\\n`;\n }\n researchContext += `\\n`;\n }\n }\n }\n\n const planningPrompt = await promptBuilder.buildPlanningPrompt(task, cwd);\n const fullPrompt = `${PLANNING_SYSTEM_PROMPT}\\n\\n${planningPrompt}\\n\\n${researchContext}`;\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, MCP resources, and ExitPlanMode\n allowedTools: [\n 'Read',\n 'Glob',\n 'Grep',\n 'WebFetch',\n 'WebSearch',\n 'ListMcpResources',\n 'ReadMcpResource',\n 'ExitPlanMode',\n 'TodoWrite',\n 'BashOutput',\n ],\n };\n\n const response = query({\n prompt: fullPrompt,\n options: { ...baseOptions, ...(options.queryOverrides || {}) },\n });\n\n const todoManager = new TodoManager(fileManager, stepLogger);\n\n let planContent = '';\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\n const todoList = await todoManager.checkAndPersistFromMessage(message, task.id);\n if (todoList) {\n emitEvent(adapter.createArtifactEvent('todos', todoList));\n }\n\n // Extract text content for plan\n if (message.type === 'assistant' && message.message?.content) {\n for (const block of message.message.content) {\n if (block.type === 'text' && block.text) {\n planContent += `${block.text}\\n`;\n }\n }\n }\n }\n\n if (planContent.trim()) {\n await fileManager.writePlan(task.id, planContent.trim());\n stepLogger.info('Plan completed', { taskId: task.id });\n }\n\n await gitManager.addAllPostHogFiles();\n await finalizeStepGitActions(context, step, {\n commitMessage: `Planning phase for ${task.title}`,\n });\n\n if (!isCloudMode) {\n emitEvent(adapter.createStatusEvent('phase_complete', { phase: 'planning' }));\n return { status: 'completed', halt: true };\n }\n\n emitEvent(adapter.createStatusEvent('phase_complete', { phase: 'planning' }));\n return { status: 'completed' };\n};\n"],"names":[],"mappings":"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"}
@@ -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 transformed = adapter.transform(message);
62
- if (transformed) {
63
- emitEvent(transformed);
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.19.0",
3
+ "version": "1.21.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",
@@ -1,4 +1,4 @@
1
- import type { AgentEvent } from '../../types.js';
1
+ import type { AgentEvent, ArtifactEvent, StatusEvent } from '../../types.js';
2
2
  import type { SDKMessage } from '@anthropic-ai/claude-agent-sdk';
3
3
  import type { ProviderAdapter } from '../types.js';
4
4
  import { ClaudeToolMapper } from './tool-mapper.js';
@@ -18,72 +18,71 @@ export class ClaudeAdapter implements ProviderAdapter {
18
18
  };
19
19
  }
20
20
 
21
- transform(sdkMessage: SDKMessage): AgentEvent | null {
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 null;
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 null;
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 null;
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 null;
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 null;
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
- return this.toolMapper.enrichToolCall(toolCallEvent);
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
- // Enrich with tool metadata
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 null;
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,21 +249,21 @@ 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 null;
263
+ return [];
254
264
  }
255
265
 
256
- createStatusEvent(phase: string, additionalData?: any): import('../../types.js').StatusEvent {
266
+ createStatusEvent(phase: string, additionalData?: any): StatusEvent {
257
267
  return {
258
268
  type: 'status',
259
269
  ts: Date.now(),
@@ -262,6 +272,15 @@ export class ClaudeAdapter implements ProviderAdapter {
262
272
  };
263
273
  }
264
274
 
275
+ createArtifactEvent(kind: string, content: any): ArtifactEvent {
276
+ return {
277
+ type: 'artifact',
278
+ ts: Date.now(),
279
+ kind,
280
+ content
281
+ };
282
+ }
283
+
265
284
  private extractUserContent(content: unknown): string | null {
266
285
  if (!content) {
267
286
  return null;
@@ -1,4 +1,4 @@
1
- import type { AgentEvent, StatusEvent } from '../types.js';
1
+ import type { AgentEvent, StatusEvent, ArtifactEvent } from '../types.js';
2
2
 
3
3
  /**
4
4
  * Provider adapter interface for transforming provider-specific messages
@@ -12,10 +12,10 @@ export interface ProviderAdapter {
12
12
  name: string;
13
13
 
14
14
  /**
15
- * Transform a provider-specific SDK message into an AgentEvent.
16
- * Returns null if the message should be ignored.
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 | null;
18
+ transform(sdkMessage: unknown): AgentEvent[];
19
19
 
20
20
  /**
21
21
  * Create a standardized status event.
@@ -23,6 +23,12 @@ export interface ProviderAdapter {
23
23
  */
24
24
  createStatusEvent(phase: string, additionalData?: any): StatusEvent;
25
25
 
26
+ /**
27
+ * Create an artifact event for custom task artifacts (todos, etc).
28
+ * Used to emit structured artifacts for UI consumption.
29
+ */
30
+ createArtifactEvent(kind: string, content: any): ArtifactEvent;
31
+
26
32
  /**
27
33
  * Create a raw SDK event for debugging purposes.
28
34
  * Wraps the original SDK message in a raw_sdk_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
- // Emit transformed event
218
- const transformedEvent = this.adapter.transform(message);
219
- if (transformedEvent) {
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(taskId: string, branchName: string, taskTitle: string, taskDescription: string): Promise<string> {
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
- // Build PR body
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 }));
@@ -19,7 +19,14 @@ Calculate an actionabilityScore (0-1) based on:
19
19
 
20
20
  If actionabilityScore < 0.7, generate specific clarifying questions to increase confidence.
21
21
 
22
- DO NOT ask questions like "how should I fix this" — focus on missing information that prevents confident planning.
22
+ Questions must present complete implementation choices, NOT request information from the user:
23
+ options: array of strings
24
+ - GOOD: options: ["Use Redux Toolkit (matches pattern in src/store/)", "Zustand (lighter weight)"]
25
+ - BAD: "Tell me which state management library to use"
26
+ - GOOD: options: ["Place in Button.tsx (existing component)", "create NewButton.tsx (separate concerns)?"]
27
+ - BAD: "Where should I put this code?"
28
+
29
+ DO NOT ask questions like "how should I fix this" or "tell me the pattern" — present concrete options that can be directly chosen and acted upon.
23
30
  </objective>
24
31
 
25
32
  <process>
@@ -60,6 +67,9 @@ Rules:
60
67
  - questions: ONLY include if actionabilityScore < 0.7
61
68
  - Each question must have 2-3 options (maximum 3)
62
69
  - Max 3 questions total
70
+ - Options must be complete, actionable choices that require NO additional user input
71
+ - NEVER use options like "Tell me the pattern", "Show me examples", "Specify the approach"
72
+ - Each option must be a full implementation decision that can be directly acted upon
63
73
  </output_format>
64
74
 
65
75
  <scoring_examples>
@@ -92,11 +102,25 @@ Questions needed: What feature? Which product area? What should it do?
92
102
  "id": "q1",
93
103
  "question": "Which caching layer should we use for API responses?",
94
104
  "options": [
95
- "Redis (existing infrastructure, requires setup)",
96
- "In-memory cache (simpler, but not distributed)",
97
- "Browser-side caching only (minimal backend changes)"
105
+ "Redis with 1-hour TTL (existing infrastructure, requires Redis client setup)",
106
+ "In-memory LRU cache with 100MB limit (simpler, single-server only)",
107
+ "HTTP Cache-Control headers only (minimal backend changes, relies on browser/CDN)"
98
108
  ]
99
109
  }
110
+ Reason: Each option is a complete, actionable decision with concrete details
111
+ </good_example>
112
+
113
+ <good_example>
114
+ {
115
+ "id": "q2",
116
+ "question": "Where should the new analytics tracking code be placed?",
117
+ "options": [
118
+ "In the existing UserAnalytics.ts module alongside page view tracking",
119
+ "Create a new EventTracking.ts module in src/analytics/ for all event tracking",
120
+ "Add directly to each component that needs tracking (no centralized module)"
121
+ ]
122
+ }
123
+ Reason: Specific file paths and architectural patterns, no user input needed
100
124
  </good_example>
101
125
 
102
126
  <bad_example>
@@ -105,7 +129,33 @@ Questions needed: What feature? Which product area? What should it do?
105
129
  "question": "How should I implement this?",
106
130
  "options": ["One way", "Another way"]
107
131
  }
108
- Reason: Too vague, doesn't explain the tradeoffs
132
+ Reason: Too vague, doesn't explain the tradeoffs or provide concrete details
133
+ </bad_example>
134
+
135
+ <bad_example>
136
+ {
137
+ "id": "q2",
138
+ "question": "Which pattern should we follow for state management?",
139
+ "options": [
140
+ "Tell me which pattern the codebase currently uses",
141
+ "Show me examples of state management",
142
+ "Whatever you think is best"
143
+ ]
144
+ }
145
+ Reason: Options request user input instead of being actionable choices. Should be concrete patterns like "Zustand stores (matching existing patterns in src/stores/)" or "React Context (simpler, no new dependencies)"
146
+ </bad_example>
147
+
148
+ <bad_example>
149
+ {
150
+ "id": "q3",
151
+ "question": "What color scheme should the button use?",
152
+ "options": [
153
+ "Use the existing theme colors",
154
+ "Let me specify custom colors",
155
+ "Match the design system"
156
+ ]
157
+ }
158
+ Reason: "Let me specify" requires user input. Should be "Primary blue (#0066FF, existing theme)" or "Secondary gray (#6B7280, existing theme)"
109
159
  </bad_example>
110
160
  </question_examples>`;
111
161