@posthog/agent 1.12.0 → 1.13.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 (219) hide show
  1. package/dist/_virtual/_commonjsHelpers.js +6 -0
  2. package/dist/_virtual/_commonjsHelpers.js.map +1 -0
  3. package/dist/_virtual/index.js +4 -0
  4. package/dist/_virtual/index.js.map +1 -0
  5. package/dist/node_modules/@ai-sdk/anthropic/dist/index.js +1154 -0
  6. package/dist/node_modules/@ai-sdk/anthropic/dist/index.js.map +1 -0
  7. package/dist/node_modules/@ai-sdk/provider/dist/index.js +296 -0
  8. package/dist/node_modules/@ai-sdk/provider/dist/index.js.map +1 -0
  9. package/dist/node_modules/@ai-sdk/provider-utils/dist/index.js +576 -0
  10. package/dist/node_modules/@ai-sdk/provider-utils/dist/index.js.map +1 -0
  11. package/dist/node_modules/@ai-sdk/ui-utils/dist/index.js +741 -0
  12. package/dist/node_modules/@ai-sdk/ui-utils/dist/index.js.map +1 -0
  13. package/dist/node_modules/@opentelemetry/api/build/esm/api/context.js +112 -0
  14. package/dist/node_modules/@opentelemetry/api/build/esm/api/context.js.map +1 -0
  15. package/dist/node_modules/@opentelemetry/api/build/esm/api/diag.js +123 -0
  16. package/dist/node_modules/@opentelemetry/api/build/esm/api/diag.js.map +1 -0
  17. package/dist/node_modules/@opentelemetry/api/build/esm/api/metrics.js +62 -0
  18. package/dist/node_modules/@opentelemetry/api/build/esm/api/metrics.js.map +1 -0
  19. package/dist/node_modules/@opentelemetry/api/build/esm/api/propagation.js +91 -0
  20. package/dist/node_modules/@opentelemetry/api/build/esm/api/propagation.js.map +1 -0
  21. package/dist/node_modules/@opentelemetry/api/build/esm/api/trace.js +79 -0
  22. package/dist/node_modules/@opentelemetry/api/build/esm/api/trace.js.map +1 -0
  23. package/dist/node_modules/@opentelemetry/api/build/esm/baggage/context-helpers.js +59 -0
  24. package/dist/node_modules/@opentelemetry/api/build/esm/baggage/context-helpers.js.map +1 -0
  25. package/dist/node_modules/@opentelemetry/api/build/esm/baggage/internal/baggage-impl.js +99 -0
  26. package/dist/node_modules/@opentelemetry/api/build/esm/baggage/internal/baggage-impl.js.map +1 -0
  27. package/dist/node_modules/@opentelemetry/api/build/esm/baggage/utils.js +31 -0
  28. package/dist/node_modules/@opentelemetry/api/build/esm/baggage/utils.js.map +1 -0
  29. package/dist/node_modules/@opentelemetry/api/build/esm/context/NoopContextManager.js +69 -0
  30. package/dist/node_modules/@opentelemetry/api/build/esm/context/NoopContextManager.js.map +1 -0
  31. package/dist/node_modules/@opentelemetry/api/build/esm/context/context.js +54 -0
  32. package/dist/node_modules/@opentelemetry/api/build/esm/context/context.js.map +1 -0
  33. package/dist/node_modules/@opentelemetry/api/build/esm/context-api.js +22 -0
  34. package/dist/node_modules/@opentelemetry/api/build/esm/context-api.js.map +1 -0
  35. package/dist/node_modules/@opentelemetry/api/build/esm/diag/ComponentLogger.js +104 -0
  36. package/dist/node_modules/@opentelemetry/api/build/esm/diag/ComponentLogger.js.map +1 -0
  37. package/dist/node_modules/@opentelemetry/api/build/esm/diag/internal/logLevelLogger.js +44 -0
  38. package/dist/node_modules/@opentelemetry/api/build/esm/diag/internal/logLevelLogger.js.map +1 -0
  39. package/dist/node_modules/@opentelemetry/api/build/esm/diag/types.js +43 -0
  40. package/dist/node_modules/@opentelemetry/api/build/esm/diag/types.js.map +1 -0
  41. package/dist/node_modules/@opentelemetry/api/build/esm/diag-api.js +27 -0
  42. package/dist/node_modules/@opentelemetry/api/build/esm/diag-api.js.map +1 -0
  43. package/dist/node_modules/@opentelemetry/api/build/esm/internal/global-utils.js +62 -0
  44. package/dist/node_modules/@opentelemetry/api/build/esm/internal/global-utils.js.map +1 -0
  45. package/dist/node_modules/@opentelemetry/api/build/esm/internal/semver.js +121 -0
  46. package/dist/node_modules/@opentelemetry/api/build/esm/internal/semver.js.map +1 -0
  47. package/dist/node_modules/@opentelemetry/api/build/esm/metrics/NoopMeter.js +167 -0
  48. package/dist/node_modules/@opentelemetry/api/build/esm/metrics/NoopMeter.js.map +1 -0
  49. package/dist/node_modules/@opentelemetry/api/build/esm/metrics/NoopMeterProvider.js +33 -0
  50. package/dist/node_modules/@opentelemetry/api/build/esm/metrics/NoopMeterProvider.js.map +1 -0
  51. package/dist/node_modules/@opentelemetry/api/build/esm/metrics-api.js +22 -0
  52. package/dist/node_modules/@opentelemetry/api/build/esm/metrics-api.js.map +1 -0
  53. package/dist/node_modules/@opentelemetry/api/build/esm/platform/node/globalThis.js +21 -0
  54. package/dist/node_modules/@opentelemetry/api/build/esm/platform/node/globalThis.js.map +1 -0
  55. package/dist/node_modules/@opentelemetry/api/build/esm/propagation/NoopTextMapPropagator.js +35 -0
  56. package/dist/node_modules/@opentelemetry/api/build/esm/propagation/NoopTextMapPropagator.js.map +1 -0
  57. package/dist/node_modules/@opentelemetry/api/build/esm/propagation/TextMapPropagator.js +40 -0
  58. package/dist/node_modules/@opentelemetry/api/build/esm/propagation/TextMapPropagator.js.map +1 -0
  59. package/dist/node_modules/@opentelemetry/api/build/esm/propagation-api.js +22 -0
  60. package/dist/node_modules/@opentelemetry/api/build/esm/propagation-api.js.map +1 -0
  61. package/dist/node_modules/@opentelemetry/api/build/esm/trace/NonRecordingSpan.js +70 -0
  62. package/dist/node_modules/@opentelemetry/api/build/esm/trace/NonRecordingSpan.js.map +1 -0
  63. package/dist/node_modules/@opentelemetry/api/build/esm/trace/NoopTracer.js +78 -0
  64. package/dist/node_modules/@opentelemetry/api/build/esm/trace/NoopTracer.js.map +1 -0
  65. package/dist/node_modules/@opentelemetry/api/build/esm/trace/NoopTracerProvider.js +34 -0
  66. package/dist/node_modules/@opentelemetry/api/build/esm/trace/NoopTracerProvider.js.map +1 -0
  67. package/dist/node_modules/@opentelemetry/api/build/esm/trace/ProxyTracer.js +55 -0
  68. package/dist/node_modules/@opentelemetry/api/build/esm/trace/ProxyTracer.js.map +1 -0
  69. package/dist/node_modules/@opentelemetry/api/build/esm/trace/ProxyTracerProvider.js +56 -0
  70. package/dist/node_modules/@opentelemetry/api/build/esm/trace/ProxyTracerProvider.js.map +1 -0
  71. package/dist/node_modules/@opentelemetry/api/build/esm/trace/context-utils.js +76 -0
  72. package/dist/node_modules/@opentelemetry/api/build/esm/trace/context-utils.js.map +1 -0
  73. package/dist/node_modules/@opentelemetry/api/build/esm/trace/invalid-span-constants.js +27 -0
  74. package/dist/node_modules/@opentelemetry/api/build/esm/trace/invalid-span-constants.js.map +1 -0
  75. package/dist/node_modules/@opentelemetry/api/build/esm/trace/spancontext-utils.js +45 -0
  76. package/dist/node_modules/@opentelemetry/api/build/esm/trace/spancontext-utils.js.map +1 -0
  77. package/dist/node_modules/@opentelemetry/api/build/esm/trace/status.js +22 -0
  78. package/dist/node_modules/@opentelemetry/api/build/esm/trace/status.js.map +1 -0
  79. package/dist/node_modules/@opentelemetry/api/build/esm/trace/trace_flags.js +25 -0
  80. package/dist/node_modules/@opentelemetry/api/build/esm/trace/trace_flags.js.map +1 -0
  81. package/dist/node_modules/@opentelemetry/api/build/esm/trace-api.js +24 -0
  82. package/dist/node_modules/@opentelemetry/api/build/esm/trace-api.js.map +1 -0
  83. package/dist/node_modules/@opentelemetry/api/build/esm/version.js +20 -0
  84. package/dist/node_modules/@opentelemetry/api/build/esm/version.js.map +1 -0
  85. package/dist/node_modules/ai/dist/index.js +2870 -0
  86. package/dist/node_modules/ai/dist/index.js.map +1 -0
  87. package/dist/node_modules/nanoid/non-secure/index.js +13 -0
  88. package/dist/node_modules/nanoid/non-secure/index.js.map +1 -0
  89. package/dist/node_modules/secure-json-parse/index.js +133 -0
  90. package/dist/node_modules/secure-json-parse/index.js.map +1 -0
  91. package/dist/node_modules/zod-to-json-schema/dist/esm/Options.js +37 -0
  92. package/dist/node_modules/zod-to-json-schema/dist/esm/Options.js.map +1 -0
  93. package/dist/node_modules/zod-to-json-schema/dist/esm/Refs.js +26 -0
  94. package/dist/node_modules/zod-to-json-schema/dist/esm/Refs.js.map +1 -0
  95. package/dist/node_modules/zod-to-json-schema/dist/esm/errorMessages.js +17 -0
  96. package/dist/node_modules/zod-to-json-schema/dist/esm/errorMessages.js.map +1 -0
  97. package/dist/node_modules/zod-to-json-schema/dist/esm/getRelativePath.js +11 -0
  98. package/dist/node_modules/zod-to-json-schema/dist/esm/getRelativePath.js.map +1 -0
  99. package/dist/node_modules/zod-to-json-schema/dist/esm/index.js +8 -0
  100. package/dist/node_modules/zod-to-json-schema/dist/esm/index.js.map +1 -0
  101. package/dist/node_modules/zod-to-json-schema/dist/esm/parseDef.js +66 -0
  102. package/dist/node_modules/zod-to-json-schema/dist/esm/parseDef.js.map +1 -0
  103. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/any.js +21 -0
  104. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/any.js.map +1 -0
  105. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/array.js +30 -0
  106. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/array.js.map +1 -0
  107. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/bigint.js +53 -0
  108. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/bigint.js.map +1 -0
  109. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/boolean.js +8 -0
  110. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/boolean.js.map +1 -0
  111. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/branded.js +8 -0
  112. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/branded.js.map +1 -0
  113. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/catch.js +8 -0
  114. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/catch.js.map +1 -0
  115. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/date.js +50 -0
  116. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/date.js.map +1 -0
  117. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/default.js +11 -0
  118. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/default.js.map +1 -0
  119. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/effects.js +11 -0
  120. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/effects.js.map +1 -0
  121. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/enum.js +9 -0
  122. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/enum.js.map +1 -0
  123. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/intersection.js +56 -0
  124. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/intersection.js.map +1 -0
  125. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/literal.js +24 -0
  126. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/literal.js.map +1 -0
  127. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/map.js +30 -0
  128. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/map.js.map +1 -0
  129. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/nativeEnum.js +19 -0
  130. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/nativeEnum.js.map +1 -0
  131. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/never.js +15 -0
  132. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/never.js.map +1 -0
  133. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/null.js +13 -0
  134. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/null.js.map +1 -0
  135. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/nullable.js +37 -0
  136. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/nullable.js.map +1 -0
  137. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/number.js +56 -0
  138. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/number.js.map +1 -0
  139. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/object.js +76 -0
  140. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/object.js.map +1 -0
  141. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/optional.js +25 -0
  142. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/optional.js.map +1 -0
  143. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/pipeline.js +24 -0
  144. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/pipeline.js.map +1 -0
  145. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/promise.js +8 -0
  146. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/promise.js.map +1 -0
  147. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/readonly.js +8 -0
  148. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/readonly.js.map +1 -0
  149. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/record.js +65 -0
  150. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/record.js.map +1 -0
  151. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/set.js +24 -0
  152. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/set.js.map +1 -0
  153. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/string.js +350 -0
  154. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/string.js.map +1 -0
  155. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/tuple.js +36 -0
  156. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/tuple.js.map +1 -0
  157. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/undefined.js +10 -0
  158. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/undefined.js.map +1 -0
  159. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/union.js +84 -0
  160. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/union.js.map +1 -0
  161. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/unknown.js +8 -0
  162. package/dist/node_modules/zod-to-json-schema/dist/esm/parsers/unknown.js.map +1 -0
  163. package/dist/node_modules/zod-to-json-schema/dist/esm/selectParser.js +110 -0
  164. package/dist/node_modules/zod-to-json-schema/dist/esm/selectParser.js.map +1 -0
  165. package/dist/node_modules/zod-to-json-schema/dist/esm/zodToJsonSchema.js +90 -0
  166. package/dist/node_modules/zod-to-json-schema/dist/esm/zodToJsonSchema.js.map +1 -0
  167. package/dist/src/agent.d.ts +3 -0
  168. package/dist/src/agent.d.ts.map +1 -1
  169. package/dist/src/agent.js +93 -291
  170. package/dist/src/agent.js.map +1 -1
  171. package/dist/src/agents/planning.d.ts +1 -1
  172. package/dist/src/agents/planning.d.ts.map +1 -1
  173. package/dist/src/agents/planning.js +1 -2
  174. package/dist/src/agents/planning.js.map +1 -1
  175. package/dist/src/agents/research.d.ts +1 -1
  176. package/dist/src/agents/research.d.ts.map +1 -1
  177. package/dist/src/agents/research.js +3 -6
  178. package/dist/src/agents/research.js.map +1 -1
  179. package/dist/src/prompt-builder.d.ts.map +1 -1
  180. package/dist/src/prompt-builder.js +0 -1
  181. package/dist/src/prompt-builder.js.map +1 -1
  182. package/dist/src/structured-extraction.d.ts +2 -2
  183. package/dist/src/structured-extraction.d.ts.map +1 -1
  184. package/dist/src/structured-extraction.js +51 -110
  185. package/dist/src/structured-extraction.js.map +1 -1
  186. package/dist/src/workflow/config.d.ts +3 -0
  187. package/dist/src/workflow/config.d.ts.map +1 -0
  188. package/dist/src/workflow/config.js +43 -0
  189. package/dist/src/workflow/config.js.map +1 -0
  190. package/dist/src/workflow/steps/build.d.ts +3 -0
  191. package/dist/src/workflow/steps/build.d.ts.map +1 -0
  192. package/dist/src/workflow/steps/build.js +64 -0
  193. package/dist/src/workflow/steps/build.js.map +1 -0
  194. package/dist/src/workflow/steps/plan.d.ts +3 -0
  195. package/dist/src/workflow/steps/plan.d.ts.map +1 -0
  196. package/dist/src/workflow/steps/plan.js +86 -0
  197. package/dist/src/workflow/steps/plan.js.map +1 -0
  198. package/dist/src/workflow/steps/research.d.ts +3 -0
  199. package/dist/src/workflow/steps/research.d.ts.map +1 -0
  200. package/dist/src/workflow/steps/research.js +124 -0
  201. package/dist/src/workflow/steps/research.js.map +1 -0
  202. package/dist/src/workflow/types.d.ts +48 -0
  203. package/dist/src/workflow/types.d.ts.map +1 -0
  204. package/dist/src/workflow/utils.d.ts +12 -0
  205. package/dist/src/workflow/utils.d.ts.map +1 -0
  206. package/dist/src/workflow/utils.js +38 -0
  207. package/dist/src/workflow/utils.js.map +1 -0
  208. package/package.json +5 -2
  209. package/src/agent.ts +112 -321
  210. package/src/agents/planning.ts +1 -2
  211. package/src/agents/research.ts +3 -6
  212. package/src/prompt-builder.ts +0 -2
  213. package/src/structured-extraction.ts +58 -115
  214. package/src/workflow/config.ts +42 -0
  215. package/src/workflow/steps/build.ts +87 -0
  216. package/src/workflow/steps/plan.ts +112 -0
  217. package/src/workflow/steps/research.ts +156 -0
  218. package/src/workflow/types.ts +53 -0
  219. package/src/workflow/utils.ts +50 -0
package/src/agent.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { query } from "@anthropic-ai/claude-agent-sdk";
2
- import type { Task, ExecutionResult, PlanResult, AgentConfig } from './types.js';
2
+ import type { Task, ExecutionResult, AgentConfig } from './types.js';
3
3
  import { TaskManager } from './task-manager.js';
4
4
  import { PostHogAPIClient } from './posthog-api.js';
5
5
  import { PostHogFileManager } from './file-manager.js';
@@ -7,12 +7,12 @@ import { GitManager } from './git-manager.js';
7
7
  import { TemplateManager } from './template-manager.js';
8
8
  import { ClaudeAdapter } from './adapters/claude/claude-adapter.js';
9
9
  import type { ProviderAdapter } from './adapters/types.js';
10
- import { PLANNING_SYSTEM_PROMPT } from './agents/planning.js';
11
- import { EXECUTION_SYSTEM_PROMPT } from './agents/execution.js';
12
10
  import { Logger } from './utils/logger.js';
13
11
  import { PromptBuilder } from './prompt-builder.js';
14
12
  import { TaskProgressReporter } from './task-progress-reporter.js';
15
- import { OpenAIExtractor, type ExtractedQuestion, type ExtractedQuestionWithAnswer } from './structured-extraction.js';
13
+ import { AISDKExtractor, type StructuredExtractor, type ExtractedQuestion, type ExtractedQuestionWithAnswer } from './structured-extraction.js';
14
+ import { TASK_WORKFLOW } from './workflow/config.js';
15
+ import type { WorkflowRuntime } from './workflow/types.js';
16
16
 
17
17
  export class Agent {
18
18
  private workingDirectory: string;
@@ -26,7 +26,7 @@ export class Agent {
26
26
  private logger: Logger;
27
27
  private progressReporter: TaskProgressReporter;
28
28
  private promptBuilder: PromptBuilder;
29
- private extractor?: OpenAIExtractor;
29
+ private extractor?: StructuredExtractor;
30
30
  private mcpServers?: Record<string, any>;
31
31
  public debug: boolean;
32
32
 
@@ -89,11 +89,7 @@ export class Agent {
89
89
  logger: this.logger.child('PromptBuilder')
90
90
  });
91
91
  this.progressReporter = new TaskProgressReporter(this.posthogAPI, this.logger);
92
-
93
- // Initialize OpenAI extractor if API key is available
94
- if (process.env.OPENAI_API_KEY) {
95
- this.extractor = new OpenAIExtractor(this.logger.child('OpenAIExtractor'));
96
- }
92
+ this.extractor = new AISDKExtractor(this.logger.child('AISDKExtractor'));
97
93
  }
98
94
 
99
95
  /**
@@ -113,6 +109,7 @@ export class Agent {
113
109
  }
114
110
 
115
111
  if (process.env.ANTHROPIC_BASE_URL && process.env.ANTHROPIC_AUTH_TOKEN) {
112
+ this.ensureOpenAIGatewayEnv();
116
113
  return;
117
114
  }
118
115
 
@@ -122,6 +119,7 @@ export class Agent {
122
119
 
123
120
  process.env.ANTHROPIC_BASE_URL = gatewayUrl;
124
121
  process.env.ANTHROPIC_AUTH_TOKEN = apiKey;
122
+ this.ensureOpenAIGatewayEnv(gatewayUrl, apiKey);
125
123
 
126
124
  this.logger.debug('Configured LLM gateway', { gatewayUrl });
127
125
  } catch (error) {
@@ -130,7 +128,7 @@ export class Agent {
130
128
  }
131
129
  }
132
130
 
133
- // Adaptive task execution - 3 phases (research → plan → build)
131
+ // Adaptive task execution orchestrated via workflow steps
134
132
  async runTask(taskOrId: Task | string, options: import('./types.js').TaskExecutionOptions = {}): Promise<void> {
135
133
  await this._configureLlmGateway();
136
134
 
@@ -142,324 +140,41 @@ export class Agent {
142
140
  this.logger.info('Starting adaptive task execution', { taskId: task.id, taskSlug, isCloudMode });
143
141
 
144
142
  // Initialize progress reporter for task run tracking (needed for PR attachment)
145
- await this.progressReporter.start(task.id, { totalSteps: 3 }); // 3 phases: research, plan, build
143
+ await this.progressReporter.start(task.id, { totalSteps: TASK_WORKFLOW.length });
146
144
  this.emitEvent(this.adapter.createStatusEvent('run_started', { runId: this.progressReporter.runId }));
147
145
 
148
- // Phase 1: Branch check
149
- const existingBranch = await this.gitManager.getTaskBranch(taskSlug);
150
- if (!existingBranch) {
151
- this.logger.info('Creating task branch', { taskSlug });
152
- const branchName = `posthog/task-${taskSlug}`;
153
- await this.gitManager.createOrSwitchToBranch(branchName);
154
- this.emitEvent(this.adapter.createStatusEvent('branch_created', { branch: branchName }));
155
-
156
- // Initial commit
157
- await this.fileManager.ensureGitignore();
158
- await this.gitManager.addAllPostHogFiles();
159
- if (isCloudMode) {
160
- await this.gitManager.commitAndPush(`Initialize task ${taskSlug}`, { allowEmpty: true });
161
- } else {
162
- await this.gitManager.commitChanges(`Initialize task ${taskSlug}`);
163
- }
164
- } else {
165
- this.logger.info('Switching to existing task branch', { branch: existingBranch });
166
- await this.gitManager.switchToBranch(existingBranch);
167
- }
168
-
169
- // Phase 2: Research
170
- const researchExists = await this.fileManager.readResearch(task.id);
171
- if (!researchExists) {
172
- this.logger.info('Starting research phase', { taskId: task.id });
173
- this.emitEvent(this.adapter.createStatusEvent('phase_start', { phase: 'research' }));
174
-
175
- // Run research agent
176
- const researchPrompt = await this.promptBuilder.buildResearchPrompt(task, cwd);
177
- const { RESEARCH_SYSTEM_PROMPT } = await import('./agents/research.js');
178
- const fullPrompt = RESEARCH_SYSTEM_PROMPT + '\n\n' + researchPrompt;
179
-
180
- const baseOptions: Record<string, any> = {
181
- model: 'claude-sonnet-4-5-20250929',
182
- cwd,
183
- permissionMode: 'plan',
184
- settingSources: ['local'],
185
- mcpServers: this.mcpServers,
186
- };
187
-
188
- const response = query({
189
- prompt: fullPrompt,
190
- options: { ...baseOptions, ...(options.queryOverrides || {}) },
191
- });
192
-
193
- let researchContent = '';
194
- for await (const message of response) {
195
- this.emitEvent(this.adapter.createRawSDKEvent(message));
196
- const transformed = this.adapter.transform(message);
197
- if (transformed) {
198
- this.emitEvent(transformed);
199
- }
200
- if (message.type === 'assistant' && message.message?.content) {
201
- for (const c of message.message.content) {
202
- if (c.type === 'text' && c.text) researchContent += c.text + '\n';
203
- }
204
- }
205
- }
146
+ await this.prepareTaskBranch(taskSlug, isCloudMode);
147
+
148
+ const workflowContext: WorkflowRuntime = {
149
+ task,
150
+ taskSlug,
151
+ cwd,
152
+ isCloudMode,
153
+ options,
154
+ logger: this.logger,
155
+ fileManager: this.fileManager,
156
+ gitManager: this.gitManager,
157
+ promptBuilder: this.promptBuilder,
158
+ progressReporter: this.progressReporter,
159
+ adapter: this.adapter,
160
+ mcpServers: this.mcpServers,
161
+ posthogAPI: this.posthogAPI,
162
+ extractor: this.extractor,
163
+ emitEvent: (event: any) => this.emitEvent(event),
164
+ stepResults: {},
165
+ };
206
166
 
207
- // Write research.md
208
- if (researchContent.trim()) {
209
- await this.fileManager.writeResearch(task.id, researchContent.trim());
210
- this.logger.info('Research completed', { taskId: task.id });
211
- }
212
-
213
- // Commit research
214
- await this.gitManager.addAllPostHogFiles();
215
-
216
- // Extract questions using structured output and save to questions.json
217
- if (this.extractor) {
218
- try {
219
- this.logger.info('Extracting questions from research.md', { taskId: task.id });
220
- const questions = await this.extractQuestionsFromResearch(task.id, false);
221
-
222
- this.logger.info('Questions extracted successfully', { taskId: task.id, count: questions.length });
223
-
224
- // Save questions.json
225
- await this.fileManager.writeQuestions(task.id, {
226
- questions,
227
- answered: false,
228
- answers: null,
229
- });
230
-
231
- this.logger.info('Questions saved to questions.json', { taskId: task.id });
232
-
233
- // Emit event for Array to pick up (local mode)
234
- if (!isCloudMode) {
235
- this.emitEvent({
236
- type: 'artifact',
237
- ts: Date.now(),
238
- kind: 'research_questions',
239
- content: questions,
240
- });
241
- this.logger.info('Emitted research_questions artifact event', { taskId: task.id });
242
- }
243
- } catch (error) {
244
- this.logger.error('Failed to extract questions', { error: error instanceof Error ? error.message : String(error) });
245
- this.emitEvent({
246
- type: 'error',
247
- ts: Date.now(),
248
- message: `Failed to extract questions: ${error instanceof Error ? error.message : String(error)}`,
249
- });
250
- }
251
- } else {
252
- this.logger.warn('OpenAI extractor not available (OPENAI_API_KEY not set), skipping question extraction');
253
- this.emitEvent({
254
- type: 'status',
255
- ts: Date.now(),
256
- phase: 'extraction_skipped',
257
- message: 'Question extraction skipped - OPENAI_API_KEY not configured',
258
- });
259
- }
260
-
261
- if (isCloudMode) {
262
- await this.gitManager.commitAndPush(`Research phase for ${task.title}`);
263
- } else {
264
- await this.gitManager.commitChanges(`Research phase for ${task.title}`);
265
- this.emitEvent(this.adapter.createStatusEvent('phase_complete', { phase: 'research' }));
266
- return; // Local mode: return to user
167
+ for (const step of TASK_WORKFLOW) {
168
+ const result = await step.run({ step, context: workflowContext });
169
+ if (result.halt) {
170
+ return;
267
171
  }
268
172
  }
269
173
 
270
- // Phase 3: Auto-answer questions (cloud mode only)
271
174
  if (isCloudMode) {
272
- const questionsData = await this.fileManager.readQuestions(task.id);
273
- if (questionsData && !questionsData.answered) {
274
- this.logger.info('Auto-answering research questions', { taskId: task.id });
275
-
276
- // Extract questions with recommended answers using structured output
277
- if (this.extractor) {
278
- const questionsWithAnswers = await this.extractQuestionsFromResearch(task.id, true) as ExtractedQuestionWithAnswer[];
279
-
280
- // Save answers to questions.json
281
- await this.fileManager.writeQuestions(task.id, {
282
- questions: questionsWithAnswers.map(qa => ({
283
- id: qa.id,
284
- question: qa.question,
285
- options: qa.options,
286
- })),
287
- answered: true,
288
- answers: questionsWithAnswers.map(qa => ({
289
- questionId: qa.id,
290
- selectedOption: qa.recommendedAnswer,
291
- customInput: qa.justification,
292
- })),
293
- });
294
-
295
- this.logger.info('Auto-answers saved to questions.json', { taskId: task.id });
296
- await this.gitManager.addAllPostHogFiles();
297
- await this.gitManager.commitAndPush(`Answer research questions for ${task.title}`);
298
- } else {
299
- this.logger.warn('OpenAI extractor not available, skipping auto-answer');
300
- }
301
- }
175
+ await this.ensurePullRequest(task, workflowContext.stepResults);
302
176
  }
303
177
 
304
- // Phase 4: Plan
305
- const planExists = await this.readPlan(task.id);
306
- if (!planExists) {
307
- // Check if questions have been answered
308
- const questionsData = await this.fileManager.readQuestions(task.id);
309
- if (!questionsData || !questionsData.answered) {
310
- this.logger.info('Waiting for user answers to research questions');
311
- this.emitEvent(this.adapter.createStatusEvent('phase_complete', { phase: 'research_questions' }));
312
- return; // Wait for user to answer questions
313
- }
314
-
315
- this.logger.info('Starting planning phase', { taskId: task.id });
316
- this.emitEvent(this.adapter.createStatusEvent('phase_start', { phase: 'planning' }));
317
-
318
- // Build context with research + questions + answers
319
- const research = await this.fileManager.readResearch(task.id);
320
- let researchContext = '';
321
- if (research) {
322
- researchContext += `## Research Analysis\n\n${research}\n\n`;
323
- }
324
-
325
- // Add questions and answers
326
- researchContext += `## Implementation Decisions\n\n`;
327
- const answers = questionsData.answers || [];
328
- for (const question of questionsData.questions) {
329
- const answer = answers.find((a: any) => a.questionId === question.id);
330
-
331
- researchContext += `### ${question.question}\n\n`;
332
- if (answer) {
333
- researchContext += `**Selected:** ${answer.selectedOption}\n`;
334
- if (answer.customInput) {
335
- researchContext += `**Details:** ${answer.customInput}\n`;
336
- }
337
- } else {
338
- this.logger.warn('No answer found for question', { questionId: question.id });
339
- researchContext += `**Selected:** Not answered\n`;
340
- }
341
- researchContext += '\n';
342
- }
343
-
344
- // Run planning agent with full context
345
- const planningPrompt = await this.promptBuilder.buildPlanningPrompt(task, cwd);
346
- const { PLANNING_SYSTEM_PROMPT } = await import('./agents/planning.js');
347
- const fullPrompt = PLANNING_SYSTEM_PROMPT + '\n\n' + planningPrompt + '\n\n' + researchContext;
348
-
349
- const baseOptions: Record<string, any> = {
350
- model: 'claude-sonnet-4-5-20250929',
351
- cwd,
352
- permissionMode: 'plan',
353
- settingSources: ['local'],
354
- mcpServers: this.mcpServers,
355
- };
356
-
357
- const response = query({
358
- prompt: fullPrompt,
359
- options: { ...baseOptions, ...(options.queryOverrides || {}) },
360
- });
361
-
362
- let planContent = '';
363
- for await (const message of response) {
364
- this.emitEvent(this.adapter.createRawSDKEvent(message));
365
- const transformed = this.adapter.transform(message);
366
- if (transformed) {
367
- this.emitEvent(transformed);
368
- }
369
- if (message.type === 'assistant' && message.message?.content) {
370
- for (const c of message.message.content) {
371
- if (c.type === 'text' && c.text) planContent += c.text + '\n';
372
- }
373
- }
374
- }
375
-
376
- // Write plan.md
377
- if (planContent.trim()) {
378
- await this.writePlan(task.id, planContent.trim());
379
- this.logger.info('Plan completed', { taskId: task.id });
380
- }
381
-
382
- // Commit plan
383
- await this.gitManager.addAllPostHogFiles();
384
- if (isCloudMode) {
385
- await this.gitManager.commitAndPush(`Planning phase for ${task.title}`);
386
- } else {
387
- await this.gitManager.commitChanges(`Planning phase for ${task.title}`);
388
- this.emitEvent(this.adapter.createStatusEvent('phase_complete', { phase: 'planning' }));
389
- return; // Local mode: return to user
390
- }
391
- }
392
-
393
- // Phase 5: Build
394
- const latestRun = task.latest_run;
395
- const prExists = latestRun?.output && (latestRun.output as any).pr_url;
396
-
397
- if (!prExists) {
398
- this.logger.info('Starting build phase', { taskId: task.id });
399
- this.emitEvent(this.adapter.createStatusEvent('phase_start', { phase: 'build' }));
400
-
401
- // Run execution agent
402
- const executionPrompt = await this.promptBuilder.buildExecutionPrompt(task, cwd);
403
- const { EXECUTION_SYSTEM_PROMPT } = await import('./agents/execution.js');
404
- const fullPrompt = EXECUTION_SYSTEM_PROMPT + '\n\n' + executionPrompt;
405
-
406
- const { PermissionMode } = await import('./types.js');
407
- const permissionMode = options.permissionMode || PermissionMode.ACCEPT_EDITS;
408
- const baseOptions: Record<string, any> = {
409
- model: 'claude-sonnet-4-5-20250929',
410
- cwd,
411
- permissionMode,
412
- settingSources: ['local'],
413
- mcpServers: this.mcpServers,
414
- };
415
-
416
- const response = query({
417
- prompt: fullPrompt,
418
- options: { ...baseOptions, ...(options.queryOverrides || {}) },
419
- });
420
-
421
- for await (const message of response) {
422
- this.emitEvent(this.adapter.createRawSDKEvent(message));
423
- const transformed = this.adapter.transform(message);
424
- if (transformed) {
425
- this.emitEvent(transformed);
426
- }
427
- }
428
-
429
- // Commit and push implementation
430
- // Stage ALL changes (not just .posthog/)
431
- const hasChanges = await this.gitManager.hasChanges();
432
- if (hasChanges) {
433
- await this.gitManager.addFiles(['.']); // Stage all changes
434
- await this.gitManager.commitChanges(`Implementation for ${task.title}`);
435
-
436
- // Push to origin
437
- const branchName = await this.gitManager.getCurrentBranch();
438
- await this.gitManager.pushBranch(branchName);
439
-
440
- this.logger.info('Implementation committed and pushed', { taskId: task.id });
441
- } else {
442
- this.logger.warn('No changes to commit in build phase', { taskId: task.id });
443
- }
444
-
445
- // Create PR
446
- const branchName = await this.gitManager.getCurrentBranch();
447
- const prUrl = await this.createPullRequest(task.id, branchName, task.title, task.description);
448
- this.logger.info('Pull request created', { taskId: task.id, prUrl });
449
- this.emitEvent(this.adapter.createStatusEvent('pr_created', { prUrl }));
450
-
451
- // Attach PR to task run
452
- try {
453
- await this.attachPullRequestToTask(task.id, prUrl, branchName);
454
- this.logger.info('PR attached to task successfully', { taskId: task.id, prUrl });
455
- } catch (error) {
456
- this.logger.warn('Could not attach PR to task', { error: error instanceof Error ? error.message : String(error) });
457
- }
458
- } else {
459
- this.logger.info('PR already exists, skipping build phase', { taskId: task.id });
460
- }
461
-
462
- // Phase 6: Complete
463
178
  await this.progressReporter.complete();
464
179
  this.logger.info('Task execution complete', { taskId: task.id });
465
180
  this.emitEvent(this.adapter.createStatusEvent('task_complete', { taskId: task.id }));
@@ -555,7 +270,7 @@ export class Agent {
555
270
  this.logger.info('Extracting questions from research.md', { taskId, includeAnswers });
556
271
 
557
272
  if (!this.extractor) {
558
- throw new Error('OpenAI extractor not initialized. Set OPENAI_API_KEY environment variable.');
273
+ throw new Error('OpenAI extractor not initialized. Ensure the LLM gateway is configured.');
559
274
  }
560
275
 
561
276
  const researchContent = await this.fileManager.readResearch(taskId);
@@ -678,6 +393,82 @@ Generated by PostHog Agent`;
678
393
  return null;
679
394
  }
680
395
 
396
+ private async prepareTaskBranch(taskSlug: string, isCloudMode: boolean): Promise<void> {
397
+ const existingBranch = await this.gitManager.getTaskBranch(taskSlug);
398
+ if (!existingBranch) {
399
+ this.logger.info('Creating task branch', { taskSlug });
400
+ const branchName = `posthog/task-${taskSlug}`;
401
+ await this.gitManager.createOrSwitchToBranch(branchName);
402
+ this.emitEvent(this.adapter.createStatusEvent('branch_created', { branch: branchName }));
403
+
404
+ await this.fileManager.ensureGitignore();
405
+ await this.gitManager.addAllPostHogFiles();
406
+ if (isCloudMode) {
407
+ await this.gitManager.commitAndPush(`Initialize task ${taskSlug}`, { allowEmpty: true });
408
+ } else {
409
+ await this.gitManager.commitChanges(`Initialize task ${taskSlug}`);
410
+ }
411
+ } else {
412
+ this.logger.info('Switching to existing task branch', { branch: existingBranch });
413
+ await this.gitManager.switchToBranch(existingBranch);
414
+ }
415
+ }
416
+
417
+ private ensureOpenAIGatewayEnv(baseUrl?: string, token?: string): void {
418
+ const resolvedBaseUrl = baseUrl || process.env.ANTHROPIC_BASE_URL;
419
+ const resolvedToken = token || process.env.ANTHROPIC_AUTH_TOKEN;
420
+
421
+ if (resolvedBaseUrl) {
422
+ process.env.OPENAI_BASE_URL = resolvedBaseUrl;
423
+ }
424
+
425
+ if (resolvedToken) {
426
+ process.env.OPENAI_API_KEY = resolvedToken;
427
+ }
428
+
429
+ if (!this.extractor) {
430
+ this.extractor = new AISDKExtractor(this.logger.child('AISDKExtractor'));
431
+ }
432
+ }
433
+
434
+ private async ensurePullRequest(task: Task, stepResults: Record<string, any>): Promise<void> {
435
+ const latestRun = task.latest_run;
436
+ const existingPr =
437
+ latestRun?.output && typeof latestRun.output === 'object'
438
+ ? (latestRun.output as any).pr_url
439
+ : null;
440
+
441
+ if (existingPr) {
442
+ this.logger.info('PR already exists, skipping creation', { taskId: task.id, prUrl: existingPr });
443
+ return;
444
+ }
445
+
446
+ const buildResult = stepResults['build'];
447
+ if (!buildResult?.commitCreated) {
448
+ this.logger.warn('Build step did not produce a commit; skipping PR creation', { taskId: task.id });
449
+ return;
450
+ }
451
+
452
+ const branchName = await this.gitManager.getCurrentBranch();
453
+ const prUrl = await this.createPullRequest(
454
+ task.id,
455
+ branchName,
456
+ task.title,
457
+ task.description ?? ''
458
+ );
459
+
460
+ this.emitEvent(this.adapter.createStatusEvent('pr_created', { prUrl }));
461
+
462
+ try {
463
+ await this.attachPullRequestToTask(task.id, prUrl, branchName);
464
+ this.logger.info('PR attached to task successfully', { taskId: task.id, prUrl });
465
+ } catch (error) {
466
+ this.logger.warn('Could not attach PR to task', {
467
+ error: error instanceof Error ? error.message : String(error),
468
+ });
469
+ }
470
+ }
471
+
681
472
  private emitEvent(event: any): void {
682
473
  if (this.debug && event.type !== 'token') {
683
474
  // Log all events except tokens (too verbose)
@@ -11,6 +11,7 @@ You are a specialized planning agent that analyzes codebases and creates detaile
11
11
  - **Read-Only Mode**: You can only read files, search code, and analyze the codebase
12
12
  - **No Modifications**: You cannot make any changes, edits, or execute commands
13
13
  - **Research Focus**: Your goal is understanding and planning, not implementation
14
+ - **Response Format**: Respond only with the markdown content above, no other text or formatting, no acknowledgement, no explanation, no nothing.
14
15
 
15
16
  ## Available Tools
16
17
 
@@ -49,8 +50,6 @@ When given a task, follow this systematic approach:
49
50
 
50
51
  ## Plan Output
51
52
 
52
- When you have completed your analysis, use the \`exit_plan_mode\` tool to present your plan. Your plan should include:
53
-
54
53
  - **Summary**: Brief overview of the implementation approach
55
54
  - **Files to Create/Modify**: Specific paths and purposes
56
55
  - **Implementation Steps**: Ordered list of actions to take
@@ -18,7 +18,6 @@ You are a research agent that explores codebases to understand implementation co
18
18
  - Code search and analysis
19
19
  - Repository structure analysis
20
20
  - Documentation review
21
- - \`create_plan\` tool for creating your research artifact
22
21
 
23
22
  ## Research Process
24
23
 
@@ -46,8 +45,6 @@ When given a task, follow this systematic approach:
46
45
 
47
46
  ## Output Format
48
47
 
49
- After completing your research, you MUST use the \`create_plan\` tool to create a research.md artifact with your questions.
50
-
51
48
  The artifact MUST follow this EXACT markdown format (this is critical for parsing):
52
49
 
53
50
  \`\`\`markdown
@@ -88,16 +85,16 @@ Based on my analysis of the codebase, here are the key questions to guide implem
88
85
 
89
86
  ## Important Requirements
90
87
 
91
- - Generate 2-5 questions (no more, no less)
88
+ - DO NOT GENERATE ANY QUESTIONS IF YOU DON'T HAVE ANY (instead say "No questions required")
89
+ - Generate up to 5 questions (no more)
92
90
  - Make options specific and reference actual code/patterns you find
93
91
  - Each question must have at least 2 concrete options plus "Something else"
94
92
  - Focus on architectural and implementation approach decisions
95
93
  - Reference specific files, components, or patterns in your options
96
94
  - Make sure the questions help guide a clear implementation path
95
+ - Respond only with the markdown content above, no other text or formatting, no acknowledgement, no explanation, no nothing.
97
96
 
98
97
  ## Final Step
99
98
 
100
- Once you have completed your research and identified the questions, use the \`create_plan\` tool to create the research.md artifact with the markdown content above. Do NOT use any other tools after creating the artifact.
101
-
102
99
  Your research should be thorough enough that the questions help clarify the user's preferences and guide the planning phase effectively.`;
103
100
 
@@ -254,8 +254,6 @@ export class PromptBuilder {
254
254
  this.logger.debug('No existing task files found for research', { taskId: task.id });
255
255
  }
256
256
 
257
- prompt += `\n\nPlease explore the codebase thoroughly and generate 3-5 clarifying questions that will help guide the implementation of this task. Use the \`create_plan\` tool to create a research.md artifact with your questions in the markdown format specified in your system prompt.`;
258
-
259
257
  return prompt;
260
258
  }
261
259