vibeman 0.0.5 → 0.0.7

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 (239) hide show
  1. package/dist/api.js +43 -0
  2. package/dist/index.js +227 -104
  3. package/dist/ui/assets/index-C_kQPI1m.js +9 -0
  4. package/dist/ui/index.html +12 -0
  5. package/package.json +13 -46
  6. package/README.md +0 -12
  7. package/dist/runtime/api/.tsbuildinfo +0 -1
  8. package/dist/runtime/api/agent/agent-service.d.ts +0 -229
  9. package/dist/runtime/api/agent/agent-service.js +0 -963
  10. package/dist/runtime/api/agent/ai-providers/amp-cli-provider.d.ts +0 -38
  11. package/dist/runtime/api/agent/ai-providers/amp-cli-provider.js +0 -268
  12. package/dist/runtime/api/agent/ai-providers/claude-code-adapter.d.ts +0 -61
  13. package/dist/runtime/api/agent/ai-providers/claude-code-adapter.js +0 -362
  14. package/dist/runtime/api/agent/ai-providers/codex-cli-provider.d.ts +0 -36
  15. package/dist/runtime/api/agent/ai-providers/codex-cli-provider.js +0 -375
  16. package/dist/runtime/api/agent/ai-providers/gemini-cli-provider.d.ts +0 -24
  17. package/dist/runtime/api/agent/ai-providers/gemini-cli-provider.js +0 -291
  18. package/dist/runtime/api/agent/ai-providers/index.d.ts +0 -9
  19. package/dist/runtime/api/agent/ai-providers/index.js +0 -9
  20. package/dist/runtime/api/agent/ai-providers/types.d.ts +0 -185
  21. package/dist/runtime/api/agent/ai-providers/types.js +0 -5
  22. package/dist/runtime/api/agent/amp-cli-provider.test.d.ts +0 -1
  23. package/dist/runtime/api/agent/amp-cli-provider.test.js +0 -99
  24. package/dist/runtime/api/agent/codex-cli-provider.test.d.ts +0 -1
  25. package/dist/runtime/api/agent/codex-cli-provider.test.js +0 -172
  26. package/dist/runtime/api/agent/core-agent-service.d.ts +0 -119
  27. package/dist/runtime/api/agent/core-agent-service.js +0 -267
  28. package/dist/runtime/api/agent/parsers.d.ts +0 -16
  29. package/dist/runtime/api/agent/parsers.js +0 -308
  30. package/dist/runtime/api/agent/prompt-service.d.ts +0 -30
  31. package/dist/runtime/api/agent/prompt-service.js +0 -452
  32. package/dist/runtime/api/agent/prompt-service.test.d.ts +0 -1
  33. package/dist/runtime/api/agent/prompt-service.test.js +0 -265
  34. package/dist/runtime/api/agent/routing-policy.d.ts +0 -171
  35. package/dist/runtime/api/agent/routing-policy.js +0 -196
  36. package/dist/runtime/api/agent/routing-policy.test.d.ts +0 -1
  37. package/dist/runtime/api/agent/routing-policy.test.js +0 -63
  38. package/dist/runtime/api/api/router-helpers.d.ts +0 -32
  39. package/dist/runtime/api/api/router-helpers.js +0 -31
  40. package/dist/runtime/api/api/routers/ai.d.ts +0 -200
  41. package/dist/runtime/api/api/routers/ai.js +0 -396
  42. package/dist/runtime/api/api/routers/executions.d.ts +0 -93
  43. package/dist/runtime/api/api/routers/executions.js +0 -94
  44. package/dist/runtime/api/api/routers/git.d.ts +0 -45
  45. package/dist/runtime/api/api/routers/git.js +0 -35
  46. package/dist/runtime/api/api/routers/provider-config.d.ts +0 -199
  47. package/dist/runtime/api/api/routers/provider-config.js +0 -252
  48. package/dist/runtime/api/api/routers/settings.d.ts +0 -158
  49. package/dist/runtime/api/api/routers/settings.js +0 -129
  50. package/dist/runtime/api/api/routers/tasks.d.ts +0 -141
  51. package/dist/runtime/api/api/routers/tasks.js +0 -238
  52. package/dist/runtime/api/api/routers/workflows.d.ts +0 -275
  53. package/dist/runtime/api/api/routers/workflows.js +0 -311
  54. package/dist/runtime/api/api/routers/worktrees.d.ts +0 -101
  55. package/dist/runtime/api/api/routers/worktrees.js +0 -80
  56. package/dist/runtime/api/api/trpc.d.ts +0 -118
  57. package/dist/runtime/api/api/trpc.js +0 -34
  58. package/dist/runtime/api/index.d.ts +0 -9
  59. package/dist/runtime/api/index.js +0 -117
  60. package/dist/runtime/api/lib/id-generator.d.ts +0 -70
  61. package/dist/runtime/api/lib/id-generator.js +0 -123
  62. package/dist/runtime/api/lib/local-config.d.ts +0 -335
  63. package/dist/runtime/api/lib/local-config.js +0 -304
  64. package/dist/runtime/api/lib/logger.d.ts +0 -11
  65. package/dist/runtime/api/lib/logger.js +0 -188
  66. package/dist/runtime/api/lib/provider-detection.d.ts +0 -61
  67. package/dist/runtime/api/lib/provider-detection.js +0 -326
  68. package/dist/runtime/api/lib/server/agent-service-singleton.d.ts +0 -6
  69. package/dist/runtime/api/lib/server/agent-service-singleton.js +0 -27
  70. package/dist/runtime/api/lib/server/bootstrap.d.ts +0 -38
  71. package/dist/runtime/api/lib/server/bootstrap.js +0 -197
  72. package/dist/runtime/api/lib/server/git-service-singleton.d.ts +0 -6
  73. package/dist/runtime/api/lib/server/git-service-singleton.js +0 -47
  74. package/dist/runtime/api/lib/server/project-root.d.ts +0 -2
  75. package/dist/runtime/api/lib/server/project-root.js +0 -61
  76. package/dist/runtime/api/lib/server/task-service-singleton.d.ts +0 -7
  77. package/dist/runtime/api/lib/server/task-service-singleton.js +0 -58
  78. package/dist/runtime/api/lib/server/vibeman-info.d.ts +0 -5
  79. package/dist/runtime/api/lib/server/vibeman-info.js +0 -85
  80. package/dist/runtime/api/lib/server/vibing-orchestrator-singleton.d.ts +0 -7
  81. package/dist/runtime/api/lib/server/vibing-orchestrator-singleton.js +0 -57
  82. package/dist/runtime/api/lib/trpc/server.d.ts +0 -965
  83. package/dist/runtime/api/lib/trpc/server.js +0 -11
  84. package/dist/runtime/api/lib/trpc/ws-server.d.ts +0 -8
  85. package/dist/runtime/api/lib/trpc/ws-server.js +0 -33
  86. package/dist/runtime/api/persistence/database-service.d.ts +0 -14
  87. package/dist/runtime/api/persistence/database-service.js +0 -74
  88. package/dist/runtime/api/persistence/execution-log-persistence.d.ts +0 -90
  89. package/dist/runtime/api/persistence/execution-log-persistence.js +0 -426
  90. package/dist/runtime/api/persistence/execution-log-persistence.test.d.ts +0 -1
  91. package/dist/runtime/api/persistence/execution-log-persistence.test.js +0 -170
  92. package/dist/runtime/api/router.d.ts +0 -968
  93. package/dist/runtime/api/router.js +0 -34
  94. package/dist/runtime/api/settings-service.d.ts +0 -110
  95. package/dist/runtime/api/settings-service.js +0 -678
  96. package/dist/runtime/api/tasks/file-watcher.d.ts +0 -23
  97. package/dist/runtime/api/tasks/file-watcher.js +0 -88
  98. package/dist/runtime/api/tasks/task-file-parser.d.ts +0 -14
  99. package/dist/runtime/api/tasks/task-file-parser.js +0 -180
  100. package/dist/runtime/api/tasks/task-service.d.ts +0 -36
  101. package/dist/runtime/api/tasks/task-service.js +0 -173
  102. package/dist/runtime/api/tasks/task-updater.d.ts +0 -62
  103. package/dist/runtime/api/tasks/task-updater.js +0 -260
  104. package/dist/runtime/api/tasks/task-updater.test.d.ts +0 -1
  105. package/dist/runtime/api/tasks/task-updater.test.js +0 -303
  106. package/dist/runtime/api/types/index.d.ts +0 -186
  107. package/dist/runtime/api/types/index.js +0 -1
  108. package/dist/runtime/api/types/settings.d.ts +0 -105
  109. package/dist/runtime/api/types/settings.js +0 -2
  110. package/dist/runtime/api/types.d.ts +0 -2
  111. package/dist/runtime/api/types.js +0 -1
  112. package/dist/runtime/api/utils/env.d.ts +0 -6
  113. package/dist/runtime/api/utils/env.js +0 -12
  114. package/dist/runtime/api/utils/stripNextEnv.d.ts +0 -7
  115. package/dist/runtime/api/utils/stripNextEnv.js +0 -22
  116. package/dist/runtime/api/utils/title-slug.d.ts +0 -6
  117. package/dist/runtime/api/utils/title-slug.js +0 -77
  118. package/dist/runtime/api/utils/url.d.ts +0 -2
  119. package/dist/runtime/api/utils/url.js +0 -19
  120. package/dist/runtime/api/vcs/git-history-service.d.ts +0 -57
  121. package/dist/runtime/api/vcs/git-history-service.js +0 -228
  122. package/dist/runtime/api/vcs/git-service.d.ts +0 -136
  123. package/dist/runtime/api/vcs/git-service.js +0 -307
  124. package/dist/runtime/api/vcs/worktree-service.d.ts +0 -93
  125. package/dist/runtime/api/vcs/worktree-service.js +0 -518
  126. package/dist/runtime/api/vcs/worktree-service.test.d.ts +0 -1
  127. package/dist/runtime/api/vcs/worktree-service.test.js +0 -20
  128. package/dist/runtime/api/workflows/quality-pipeline.d.ts +0 -58
  129. package/dist/runtime/api/workflows/quality-pipeline.js +0 -401
  130. package/dist/runtime/api/workflows/vibing-orchestrator.d.ts +0 -406
  131. package/dist/runtime/api/workflows/vibing-orchestrator.js +0 -2462
  132. package/dist/runtime/api/workflows/workflow-effects.d.ts +0 -45
  133. package/dist/runtime/api/workflows/workflow-effects.js +0 -49
  134. package/dist/runtime/api/workflows/workflow-reconciler.d.ts +0 -65
  135. package/dist/runtime/api/workflows/workflow-reconciler.js +0 -226
  136. package/dist/runtime/api/workflows/workflow-reducer.d.ts +0 -26
  137. package/dist/runtime/api/workflows/workflow-reducer.js +0 -288
  138. package/dist/runtime/api/workflows/workflow-reducer.test.d.ts +0 -1
  139. package/dist/runtime/api/workflows/workflow-reducer.test.js +0 -247
  140. package/dist/runtime/api/workflows/workflow-schema.d.ts +0 -546
  141. package/dist/runtime/api/workflows/workflow-schema.js +0 -256
  142. package/dist/runtime/web/.next/BUILD_ID +0 -1
  143. package/dist/runtime/web/.next/app-build-manifest.json +0 -66
  144. package/dist/runtime/web/.next/app-path-routes-manifest.json +0 -8
  145. package/dist/runtime/web/.next/build-manifest.json +0 -33
  146. package/dist/runtime/web/.next/package.json +0 -1
  147. package/dist/runtime/web/.next/prerender-manifest.json +0 -61
  148. package/dist/runtime/web/.next/react-loadable-manifest.json +0 -8
  149. package/dist/runtime/web/.next/required-server-files.json +0 -334
  150. package/dist/runtime/web/.next/routes-manifest.json +0 -70
  151. package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route.js +0 -1
  152. package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route.js.nft.json +0 -1
  153. package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route_client-reference-manifest.js +0 -1
  154. package/dist/runtime/web/.next/server/app/_not-found/page.js +0 -2
  155. package/dist/runtime/web/.next/server/app/_not-found/page.js.nft.json +0 -1
  156. package/dist/runtime/web/.next/server/app/_not-found/page_client-reference-manifest.js +0 -1
  157. package/dist/runtime/web/.next/server/app/_not-found.html +0 -7
  158. package/dist/runtime/web/.next/server/app/_not-found.meta +0 -8
  159. package/dist/runtime/web/.next/server/app/_not-found.rsc +0 -22
  160. package/dist/runtime/web/.next/server/app/api/health/route.js +0 -1
  161. package/dist/runtime/web/.next/server/app/api/health/route.js.nft.json +0 -1
  162. package/dist/runtime/web/.next/server/app/api/health/route_client-reference-manifest.js +0 -1
  163. package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js +0 -1
  164. package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js.nft.json +0 -1
  165. package/dist/runtime/web/.next/server/app/api/images/[...path]/route_client-reference-manifest.js +0 -1
  166. package/dist/runtime/web/.next/server/app/api/upload/route.js +0 -1
  167. package/dist/runtime/web/.next/server/app/api/upload/route.js.nft.json +0 -1
  168. package/dist/runtime/web/.next/server/app/api/upload/route_client-reference-manifest.js +0 -1
  169. package/dist/runtime/web/.next/server/app/index.html +0 -7
  170. package/dist/runtime/web/.next/server/app/index.meta +0 -7
  171. package/dist/runtime/web/.next/server/app/index.rsc +0 -27
  172. package/dist/runtime/web/.next/server/app/page.js +0 -112
  173. package/dist/runtime/web/.next/server/app/page.js.nft.json +0 -1
  174. package/dist/runtime/web/.next/server/app/page_client-reference-manifest.js +0 -1
  175. package/dist/runtime/web/.next/server/app-paths-manifest.json +0 -8
  176. package/dist/runtime/web/.next/server/chunks/210.js +0 -1
  177. package/dist/runtime/web/.next/server/chunks/291.js +0 -18
  178. package/dist/runtime/web/.next/server/chunks/552.js +0 -22
  179. package/dist/runtime/web/.next/server/chunks/780.js +0 -1
  180. package/dist/runtime/web/.next/server/chunks/905.js +0 -6
  181. package/dist/runtime/web/.next/server/chunks/98.js +0 -1
  182. package/dist/runtime/web/.next/server/functions-config-manifest.json +0 -4
  183. package/dist/runtime/web/.next/server/middleware-build-manifest.js +0 -1
  184. package/dist/runtime/web/.next/server/middleware-manifest.json +0 -6
  185. package/dist/runtime/web/.next/server/middleware-react-loadable-manifest.js +0 -1
  186. package/dist/runtime/web/.next/server/next-font-manifest.js +0 -1
  187. package/dist/runtime/web/.next/server/next-font-manifest.json +0 -1
  188. package/dist/runtime/web/.next/server/pages/404.html +0 -7
  189. package/dist/runtime/web/.next/server/pages/500.html +0 -1
  190. package/dist/runtime/web/.next/server/pages/_app.js +0 -1
  191. package/dist/runtime/web/.next/server/pages/_app.js.nft.json +0 -1
  192. package/dist/runtime/web/.next/server/pages/_document.js +0 -1
  193. package/dist/runtime/web/.next/server/pages/_document.js.nft.json +0 -1
  194. package/dist/runtime/web/.next/server/pages/_error.js +0 -19
  195. package/dist/runtime/web/.next/server/pages/_error.js.nft.json +0 -1
  196. package/dist/runtime/web/.next/server/pages-manifest.json +0 -6
  197. package/dist/runtime/web/.next/server/server-reference-manifest.js +0 -1
  198. package/dist/runtime/web/.next/server/server-reference-manifest.json +0 -1
  199. package/dist/runtime/web/.next/server/webpack-runtime.js +0 -1
  200. package/dist/runtime/web/.next/static/LJFZk_8tvKFN_Ee4HqUuM/_buildManifest.js +0 -1
  201. package/dist/runtime/web/.next/static/LJFZk_8tvKFN_Ee4HqUuM/_ssgManifest.js +0 -1
  202. package/dist/runtime/web/.next/static/chunks/05c91ade-7d09b2b280adffd1.js +0 -1
  203. package/dist/runtime/web/.next/static/chunks/201-51bef3fa8c832e2e.js +0 -1
  204. package/dist/runtime/web/.next/static/chunks/524-89747ed9b0294f8a.js +0 -1
  205. package/dist/runtime/web/.next/static/chunks/554-8bec6e9cca6acc67.js +0 -1
  206. package/dist/runtime/web/.next/static/chunks/764.86e9503a69d45a85.js +0 -1
  207. package/dist/runtime/web/.next/static/chunks/7ab4dc20-239138e0ae7af24a.js +0 -1
  208. package/dist/runtime/web/.next/static/chunks/905-342391e3d3a3678f.js +0 -20
  209. package/dist/runtime/web/.next/static/chunks/a8a5ce16-4edea7df2d9b544a.js +0 -79
  210. package/dist/runtime/web/.next/static/chunks/ad74d572-4c1b162e2c15acaa.js +0 -1
  211. package/dist/runtime/web/.next/static/chunks/app/.vibeman/assets/images/[...path]/route-7b752a8641f96c1f.js +0 -1
  212. package/dist/runtime/web/.next/static/chunks/app/_not-found/page-34e66b251c2b5044.js +0 -1
  213. package/dist/runtime/web/.next/static/chunks/app/api/health/route-7b752a8641f96c1f.js +0 -1
  214. package/dist/runtime/web/.next/static/chunks/app/api/images/[...path]/route-7b752a8641f96c1f.js +0 -1
  215. package/dist/runtime/web/.next/static/chunks/app/api/upload/route-7b752a8641f96c1f.js +0 -1
  216. package/dist/runtime/web/.next/static/chunks/app/layout-df9ac93cb02b2385.js +0 -1
  217. package/dist/runtime/web/.next/static/chunks/app/page-6610743f7de5f92a.js +0 -1
  218. package/dist/runtime/web/.next/static/chunks/c25e0690-e9b798b8de667da1.js +0 -1
  219. package/dist/runtime/web/.next/static/chunks/framework-57157ec4d37f64aa.js +0 -1
  220. package/dist/runtime/web/.next/static/chunks/main-app-156cc0c60371bd78.js +0 -1
  221. package/dist/runtime/web/.next/static/chunks/main-df25d367c47b1fec.js +0 -1
  222. package/dist/runtime/web/.next/static/chunks/pages/_app-9f629a5e1131d19f.js +0 -1
  223. package/dist/runtime/web/.next/static/chunks/pages/_error-9238238274c7efcd.js +0 -1
  224. package/dist/runtime/web/.next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
  225. package/dist/runtime/web/.next/static/chunks/webpack-cd50e39b423d1808.js +0 -1
  226. package/dist/runtime/web/.next/static/css/2728291c68f99cb1.css +0 -3
  227. package/dist/runtime/web/.next/static/css/4fbf378a264bd4ea.css +0 -1
  228. package/dist/runtime/web/.next/static/css/521bd69cc298cd1a.css +0 -1
  229. package/dist/runtime/web/.next/static/css/537e22821e101b87.css +0 -1
  230. package/dist/runtime/web/.next/static/media/19cfc7226ec3afaa-s.woff2 +0 -0
  231. package/dist/runtime/web/.next/static/media/21350d82a1f187e9-s.woff2 +0 -0
  232. package/dist/runtime/web/.next/static/media/8e9860b6e62d6359-s.woff2 +0 -0
  233. package/dist/runtime/web/.next/static/media/ba9851c3c22cd980-s.woff2 +0 -0
  234. package/dist/runtime/web/.next/static/media/c5fe6dc8356a8c31-s.woff2 +0 -0
  235. package/dist/runtime/web/.next/static/media/df0a9ae256c0569c-s.woff2 +0 -0
  236. package/dist/runtime/web/.next/static/media/e4af272ccee01ff0-s.p.woff2 +0 -0
  237. package/dist/runtime/web/package.json +0 -65
  238. package/dist/runtime/web/server.js +0 -44
  239. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -1,45 +0,0 @@
1
- import type { WorkflowState } from './workflow-schema.js';
2
- export type WorkflowEffect = {
3
- type: 'EXECUTE_IMPLEMENTATION';
4
- feedback?: string;
5
- } | {
6
- type: 'EXECUTE_VALIDATION';
7
- } | {
8
- type: 'EXECUTE_AI_REVIEW';
9
- } | {
10
- type: 'EXECUTE_MERGE';
11
- } | {
12
- type: 'EXECUTE_CLEANUP';
13
- } | {
14
- type: 'STOP_EXECUTION';
15
- } | {
16
- type: 'SAVE_WORKFLOW';
17
- } | {
18
- type: 'UPDATE_TASK';
19
- } | {
20
- type: 'EMIT_EVENT';
21
- name: string;
22
- payload: Record<string, unknown>;
23
- };
24
- export interface EffectContext {
25
- workflowId: string;
26
- taskId: string;
27
- state: WorkflowState;
28
- executeImplementation: (feedback?: string) => Promise<void>;
29
- executeValidation: () => Promise<void>;
30
- executeAiReview: () => Promise<void>;
31
- executeMerge: () => Promise<void>;
32
- executeCleanup: () => Promise<void>;
33
- stopExecution: () => Promise<void>;
34
- saveWorkflow: (state: WorkflowState) => Promise<void>;
35
- updateTask: () => Promise<void>;
36
- emit: (name: string, payload: Record<string, unknown>) => void;
37
- }
38
- export declare function executeEffects(effects: WorkflowEffect[], context: EffectContext): Promise<void>;
39
- export interface EffectLogEntry {
40
- timestamp: string;
41
- effect: WorkflowEffect;
42
- workflowId: string;
43
- phase: string;
44
- }
45
- export declare function createEffectLog(effects: WorkflowEffect[], state: WorkflowState): EffectLogEntry[];
@@ -1,49 +0,0 @@
1
- // ============================================================================
2
- // EFFECT EXECUTOR
3
- // Executes a list of effects using the provided context
4
- // ============================================================================
5
- export async function executeEffects(effects, context) {
6
- for (const effect of effects) {
7
- await executeEffect(effect, context);
8
- }
9
- }
10
- async function executeEffect(effect, context) {
11
- switch (effect.type) {
12
- case 'EXECUTE_IMPLEMENTATION':
13
- await context.executeImplementation(effect.feedback);
14
- break;
15
- case 'EXECUTE_VALIDATION':
16
- await context.executeValidation();
17
- break;
18
- case 'EXECUTE_AI_REVIEW':
19
- await context.executeAiReview();
20
- break;
21
- case 'EXECUTE_MERGE':
22
- await context.executeMerge();
23
- break;
24
- case 'EXECUTE_CLEANUP':
25
- await context.executeCleanup();
26
- break;
27
- case 'STOP_EXECUTION':
28
- await context.stopExecution();
29
- break;
30
- case 'SAVE_WORKFLOW':
31
- await context.saveWorkflow(context.state);
32
- break;
33
- case 'UPDATE_TASK':
34
- await context.updateTask();
35
- break;
36
- case 'EMIT_EVENT':
37
- context.emit(effect.name, effect.payload);
38
- break;
39
- }
40
- }
41
- export function createEffectLog(effects, state) {
42
- const now = new Date().toISOString();
43
- return effects.map((effect) => ({
44
- timestamp: now,
45
- effect,
46
- workflowId: state.id,
47
- phase: state.phase,
48
- }));
49
- }
@@ -1,65 +0,0 @@
1
- import { EventEmitter } from 'events';
2
- export interface ReconcilerConfig {
3
- checkIntervalMs: number;
4
- stuckThresholdMs: number;
5
- maxReconcileAttempts: number;
6
- }
7
- export interface WorkflowSnapshot {
8
- id: string;
9
- taskId: string;
10
- phase: string;
11
- lastUpdatedAt: string;
12
- terminalStatus: string | null;
13
- lastHeartbeat?: string;
14
- reconcileAttempts?: number;
15
- }
16
- export interface ReconcileAction {
17
- workflowId: string;
18
- action: 'timeout' | 'retry' | 'fail' | 'skip';
19
- reason: string;
20
- timestamp: string;
21
- }
22
- export declare class WorkflowReconciler extends EventEmitter {
23
- private config;
24
- private intervalId;
25
- private running;
26
- private reconcileHistory;
27
- private getWorkflowsFn;
28
- private onStuckWorkflow;
29
- constructor(config: Partial<ReconcilerConfig>, getWorkflows: () => WorkflowSnapshot[], onStuckWorkflow: (workflowId: string, action: ReconcileAction) => Promise<void>);
30
- start(): void;
31
- stop(): void;
32
- isRunning(): boolean;
33
- getReconcileHistory(): ReconcileAction[];
34
- reconcile(): Promise<ReconcileAction[]>;
35
- private checkWorkflow;
36
- }
37
- export declare class HeartbeatTracker {
38
- private heartbeats;
39
- private staleThresholdMs;
40
- constructor(staleThresholdMs?: number);
41
- recordHeartbeat(workflowId: string, phase: string): void;
42
- getLastHeartbeat(workflowId: string): string | undefined;
43
- isStale(workflowId: string): boolean;
44
- remove(workflowId: string): void;
45
- getStaleWorkflows(): string[];
46
- clear(): void;
47
- }
48
- export declare class IdempotencyGuard {
49
- private processedEvents;
50
- private ttlMs;
51
- private cleanupIntervalId;
52
- constructor(ttlMs?: number);
53
- start(): void;
54
- stop(): void;
55
- generateEventKey(workflowId: string, eventType: string, payload?: Record<string, unknown>): string;
56
- isProcessed(eventKey: string): boolean;
57
- markProcessed(eventKey: string, result: 'success' | 'failure'): void;
58
- getProcessedResult(eventKey: string): 'success' | 'failure' | null;
59
- withIdempotency<T>(eventKey: string, operation: () => Promise<T>): Promise<{
60
- result: T;
61
- skipped: boolean;
62
- }>;
63
- private cleanup;
64
- clear(): void;
65
- }
@@ -1,226 +0,0 @@
1
- import { EventEmitter } from 'events';
2
- import { log } from '../lib/logger.js';
3
- const DEFAULT_CONFIG = {
4
- checkIntervalMs: 60 * 1000,
5
- stuckThresholdMs: 30 * 60 * 1000,
6
- maxReconcileAttempts: 3,
7
- };
8
- export class WorkflowReconciler extends EventEmitter {
9
- constructor(config, getWorkflows, onStuckWorkflow) {
10
- super();
11
- this.intervalId = null;
12
- this.running = false;
13
- this.reconcileHistory = [];
14
- this.config = { ...DEFAULT_CONFIG, ...config };
15
- this.getWorkflowsFn = getWorkflows;
16
- this.onStuckWorkflow = onStuckWorkflow;
17
- }
18
- start() {
19
- if (this.running)
20
- return;
21
- this.running = true;
22
- log.info('Workflow reconciler started', {
23
- checkIntervalMs: this.config.checkIntervalMs,
24
- stuckThresholdMs: this.config.stuckThresholdMs,
25
- }, 'workflow-reconciler');
26
- this.intervalId = setInterval(() => {
27
- this.reconcile().catch((err) => {
28
- log.error('Reconciler tick failed', err, 'workflow-reconciler');
29
- });
30
- }, this.config.checkIntervalMs);
31
- this.reconcile().catch((err) => {
32
- log.error('Initial reconcile failed', err, 'workflow-reconciler');
33
- });
34
- }
35
- stop() {
36
- if (!this.running)
37
- return;
38
- this.running = false;
39
- if (this.intervalId) {
40
- clearInterval(this.intervalId);
41
- this.intervalId = null;
42
- }
43
- log.info('Workflow reconciler stopped', {}, 'workflow-reconciler');
44
- }
45
- isRunning() {
46
- return this.running;
47
- }
48
- getReconcileHistory() {
49
- return [...this.reconcileHistory];
50
- }
51
- async reconcile() {
52
- const workflows = this.getWorkflowsFn();
53
- const now = Date.now();
54
- const actions = [];
55
- for (const wf of workflows) {
56
- const action = this.checkWorkflow(wf, now);
57
- if (action) {
58
- actions.push(action);
59
- this.reconcileHistory.push(action);
60
- if (this.reconcileHistory.length > 100) {
61
- this.reconcileHistory = this.reconcileHistory.slice(-100);
62
- }
63
- try {
64
- await this.onStuckWorkflow(wf.id, action);
65
- this.emit('workflowReconciled', action);
66
- }
67
- catch (err) {
68
- log.error('Failed to handle stuck workflow', { workflowId: wf.id, err }, 'workflow-reconciler');
69
- }
70
- }
71
- }
72
- if (actions.length > 0) {
73
- log.info('Reconciler found stuck workflows', { count: actions.length }, 'workflow-reconciler');
74
- }
75
- return actions;
76
- }
77
- checkWorkflow(wf, now) {
78
- if (wf.terminalStatus === 'paused' || wf.terminalStatus === 'failed') {
79
- return null;
80
- }
81
- if (wf.phase === 'done') {
82
- return null;
83
- }
84
- const activePhases = ['implementing', 'validating', 'merging'];
85
- if (!activePhases.includes(wf.phase)) {
86
- return null;
87
- }
88
- const lastUpdate = new Date(wf.lastHeartbeat || wf.lastUpdatedAt).getTime();
89
- const elapsed = now - lastUpdate;
90
- if (elapsed < this.config.stuckThresholdMs) {
91
- return null;
92
- }
93
- const attempts = wf.reconcileAttempts || 0;
94
- const timestamp = new Date().toISOString();
95
- if (attempts >= this.config.maxReconcileAttempts) {
96
- return {
97
- workflowId: wf.id,
98
- action: 'fail',
99
- reason: `Exceeded max reconcile attempts (${this.config.maxReconcileAttempts}) after ${Math.round(elapsed / 60000)}min stuck`,
100
- timestamp,
101
- };
102
- }
103
- return {
104
- workflowId: wf.id,
105
- action: 'timeout',
106
- reason: `No progress for ${Math.round(elapsed / 60000)}min in phase ${wf.phase}`,
107
- timestamp,
108
- };
109
- }
110
- }
111
- // ============================================================================
112
- // HEARTBEAT TRACKER
113
- // Tracks heartbeats for active workflow executions
114
- // ============================================================================
115
- export class HeartbeatTracker {
116
- constructor(staleThresholdMs = 5 * 60 * 1000) {
117
- this.heartbeats = new Map();
118
- this.staleThresholdMs = staleThresholdMs;
119
- }
120
- recordHeartbeat(workflowId, phase) {
121
- this.heartbeats.set(workflowId, {
122
- lastBeat: new Date().toISOString(),
123
- phase,
124
- });
125
- }
126
- getLastHeartbeat(workflowId) {
127
- return this.heartbeats.get(workflowId)?.lastBeat;
128
- }
129
- isStale(workflowId) {
130
- const entry = this.heartbeats.get(workflowId);
131
- if (!entry)
132
- return true;
133
- const elapsed = Date.now() - new Date(entry.lastBeat).getTime();
134
- return elapsed > this.staleThresholdMs;
135
- }
136
- remove(workflowId) {
137
- this.heartbeats.delete(workflowId);
138
- }
139
- getStaleWorkflows() {
140
- const stale = [];
141
- const now = Date.now();
142
- for (const [id, entry] of this.heartbeats) {
143
- const elapsed = now - new Date(entry.lastBeat).getTime();
144
- if (elapsed > this.staleThresholdMs) {
145
- stale.push(id);
146
- }
147
- }
148
- return stale;
149
- }
150
- clear() {
151
- this.heartbeats.clear();
152
- }
153
- }
154
- // ============================================================================
155
- // IDEMPOTENCY GUARD
156
- // Ensures workflow events are processed only once
157
- // ============================================================================
158
- export class IdempotencyGuard {
159
- constructor(ttlMs = 60 * 60 * 1000) {
160
- this.processedEvents = new Map();
161
- this.cleanupIntervalId = null;
162
- this.ttlMs = ttlMs;
163
- }
164
- start() {
165
- this.cleanupIntervalId = setInterval(() => {
166
- this.cleanup();
167
- }, this.ttlMs / 2);
168
- }
169
- stop() {
170
- if (this.cleanupIntervalId) {
171
- clearInterval(this.cleanupIntervalId);
172
- this.cleanupIntervalId = null;
173
- }
174
- }
175
- generateEventKey(workflowId, eventType, payload) {
176
- const payloadHash = payload ? JSON.stringify(payload).slice(0, 100) : '';
177
- return `${workflowId}:${eventType}:${payloadHash}`;
178
- }
179
- isProcessed(eventKey) {
180
- return this.processedEvents.has(eventKey);
181
- }
182
- markProcessed(eventKey, result) {
183
- this.processedEvents.set(eventKey, {
184
- timestamp: new Date().toISOString(),
185
- result,
186
- });
187
- }
188
- getProcessedResult(eventKey) {
189
- return this.processedEvents.get(eventKey)?.result ?? null;
190
- }
191
- async withIdempotency(eventKey, operation) {
192
- if (this.isProcessed(eventKey)) {
193
- const prevResult = this.getProcessedResult(eventKey);
194
- log.debug('Skipping duplicate event', { eventKey, previousResult: prevResult }, 'idempotency-guard');
195
- return { result: undefined, skipped: true };
196
- }
197
- try {
198
- const result = await operation();
199
- this.markProcessed(eventKey, 'success');
200
- return { result, skipped: false };
201
- }
202
- catch (err) {
203
- this.markProcessed(eventKey, 'failure');
204
- throw err;
205
- }
206
- }
207
- cleanup() {
208
- const now = Date.now();
209
- const toRemove = [];
210
- for (const [key, entry] of this.processedEvents) {
211
- const elapsed = now - new Date(entry.timestamp).getTime();
212
- if (elapsed > this.ttlMs) {
213
- toRemove.push(key);
214
- }
215
- }
216
- for (const key of toRemove) {
217
- this.processedEvents.delete(key);
218
- }
219
- if (toRemove.length > 0) {
220
- log.debug('Cleaned up expired idempotency keys', { count: toRemove.length }, 'idempotency-guard');
221
- }
222
- }
223
- clear() {
224
- this.processedEvents.clear();
225
- }
226
- }
@@ -1,26 +0,0 @@
1
- import type { WorkflowState, WorkflowEvent, WorkflowConfig } from './workflow-schema.js';
2
- import type { WorkflowEffect } from './workflow-effects.js';
3
- export interface ReducerResult {
4
- state: WorkflowState;
5
- effects: WorkflowEffect[];
6
- }
7
- export declare function reduce(state: WorkflowState, event: WorkflowEvent): ReducerResult;
8
- export type NextAction = {
9
- action: 'implementing';
10
- feedback?: string;
11
- } | {
12
- action: 'validating';
13
- } | {
14
- action: 'ai-reviewing';
15
- } | {
16
- action: 'awaiting-approval';
17
- } | {
18
- action: 'merging';
19
- } | {
20
- action: 'cleanup';
21
- } | {
22
- action: 'none';
23
- reason: string;
24
- };
25
- export declare function computeNextAction(state: WorkflowState): NextAction;
26
- export declare function createInitialState(workflowId: string, taskId: string, config: WorkflowConfig): WorkflowState;
@@ -1,288 +0,0 @@
1
- import { getNextPhase } from './workflow-schema.js';
2
- export function reduce(state, event) {
3
- const now = new Date().toISOString();
4
- const nextPhase = getNextPhase(state, event);
5
- // Invalid transition - no change
6
- if (nextPhase === null) {
7
- return { state, effects: [] };
8
- }
9
- // Compute new state based on event type
10
- let newState = {
11
- ...state,
12
- lastUpdatedAt: now,
13
- };
14
- const effects = [];
15
- // Handle terminal statuses
16
- if (nextPhase === 'paused') {
17
- newState = {
18
- ...newState,
19
- terminalStatus: 'paused',
20
- error: 'Paused by user',
21
- };
22
- effects.push({ type: 'STOP_EXECUTION' });
23
- effects.push({ type: 'EMIT_EVENT', name: 'workflowPaused', payload: { workflowId: state.id } });
24
- effects.push({ type: 'SAVE_WORKFLOW' });
25
- return { state: newState, effects };
26
- }
27
- if (nextPhase === 'failed') {
28
- const errorMsg = 'error' in event ? event.error : 'Unknown error';
29
- newState = {
30
- ...newState,
31
- terminalStatus: 'failed',
32
- error: errorMsg,
33
- failureContext: {
34
- atPhase: state.phase,
35
- error: errorMsg,
36
- timestamp: now,
37
- },
38
- };
39
- effects.push({
40
- type: 'EMIT_EVENT',
41
- name: 'workflowFailed',
42
- payload: { workflowId: state.id, error: errorMsg },
43
- });
44
- effects.push({ type: 'SAVE_WORKFLOW' });
45
- return { state: newState, effects };
46
- }
47
- // Clear terminal status if resuming
48
- if (event.type === 'RESUME' || event.type === 'RETRY') {
49
- newState = {
50
- ...newState,
51
- terminalStatus: null,
52
- error: undefined,
53
- };
54
- }
55
- // Phase transition
56
- if (nextPhase !== state.phase) {
57
- newState = {
58
- ...newState,
59
- phase: nextPhase,
60
- };
61
- effects.push({
62
- type: 'EMIT_EVENT',
63
- name: 'workflowPhaseChanged',
64
- payload: { workflowId: state.id, oldPhase: state.phase, newPhase: nextPhase },
65
- });
66
- }
67
- // Event-specific state updates and effects
68
- switch (event.type) {
69
- case 'START':
70
- effects.push({ type: 'EXECUTE_IMPLEMENTATION' });
71
- break;
72
- case 'IMPL_SUCCESS':
73
- if (event.executionId) {
74
- newState = updateExecutionIds(newState, 'implementing', event.executionId);
75
- }
76
- if (!state.config.stepByStepMode) {
77
- effects.push({ type: 'EXECUTE_VALIDATION' });
78
- }
79
- break;
80
- case 'IMPL_FAIL':
81
- newState = incrementAttempts(newState, 'implementing');
82
- if (nextPhase === 'implementing' && event.canRetry) {
83
- newState = {
84
- ...newState,
85
- rerunContext: {
86
- reason: event.error,
87
- previousExecutionId: getLastExecutionId(state, 'implementing'),
88
- },
89
- };
90
- if (!state.config.stepByStepMode) {
91
- effects.push({ type: 'EXECUTE_IMPLEMENTATION', feedback: event.error });
92
- }
93
- }
94
- break;
95
- case 'VALID_SUCCESS':
96
- newState = {
97
- ...newState,
98
- qualityResults: event.results,
99
- };
100
- if (nextPhase === 'approved' && state.config.aiCodeReview && !state.config.stepByStepMode) {
101
- effects.push({ type: 'EXECUTE_AI_REVIEW' });
102
- }
103
- if (nextPhase === 'merging' && !state.config.stepByStepMode) {
104
- effects.push({ type: 'EXECUTE_MERGE' });
105
- }
106
- break;
107
- case 'VALID_FAIL':
108
- if (event.results) {
109
- newState = { ...newState, qualityResults: event.results };
110
- }
111
- newState = {
112
- ...newState,
113
- rerunContext: {
114
- reason: `Validation failed: ${event.error}`,
115
- previousExecutionId: getLastExecutionId(state, 'implementing'),
116
- },
117
- };
118
- if (!state.config.stepByStepMode) {
119
- effects.push({ type: 'EXECUTE_IMPLEMENTATION', feedback: event.error });
120
- }
121
- break;
122
- case 'REVIEW_DONE':
123
- newState = {
124
- ...newState,
125
- aiReviewResult: event.result,
126
- };
127
- if (event.result.executionId) {
128
- newState = updateExecutionIds(newState, 'approved', event.result.executionId);
129
- }
130
- effects.push({ type: 'UPDATE_TASK' });
131
- break;
132
- case 'REVIEW_SKIP':
133
- effects.push({ type: 'UPDATE_TASK' });
134
- break;
135
- case 'APPROVE':
136
- if (!state.config.stepByStepMode) {
137
- effects.push({ type: 'EXECUTE_MERGE' });
138
- }
139
- break;
140
- case 'REJECT':
141
- newState = {
142
- ...newState,
143
- rerunContext: {
144
- reason: 'Rejected by reviewer',
145
- feedback: event.feedback,
146
- previousExecutionId: getLastExecutionId(state, 'implementing'),
147
- },
148
- };
149
- if (!state.config.stepByStepMode) {
150
- effects.push({ type: 'EXECUTE_IMPLEMENTATION', feedback: event.feedback });
151
- }
152
- break;
153
- case 'MERGE_SUCCESS':
154
- newState = {
155
- ...newState,
156
- links: {
157
- ...newState.links,
158
- prUrl: event.prUrl,
159
- prNumber: event.prNumber,
160
- },
161
- endTime: now,
162
- };
163
- effects.push({ type: 'EXECUTE_CLEANUP' });
164
- effects.push({
165
- type: 'EMIT_EVENT',
166
- name: 'workflowCompleted',
167
- payload: { workflowId: state.id },
168
- });
169
- break;
170
- case 'MERGE_FAIL':
171
- // Stay in approved, let user retry
172
- break;
173
- case 'CLEAN_SUCCESS':
174
- newState = { ...newState, endTime: now };
175
- break;
176
- case 'COMPLETE_MANUAL':
177
- newState = {
178
- ...newState,
179
- phase: 'done',
180
- endTime: now,
181
- };
182
- effects.push({
183
- type: 'EMIT_EVENT',
184
- name: 'workflowCompleted',
185
- payload: { workflowId: state.id, manual: true },
186
- });
187
- break;
188
- case 'RETRY':
189
- newState = {
190
- ...newState,
191
- rerunContext: {
192
- reason: 'Manual retry',
193
- previousExecutionId: getLastExecutionId(state, 'implementing'),
194
- },
195
- };
196
- effects.push({ type: 'EXECUTE_IMPLEMENTATION' });
197
- break;
198
- }
199
- effects.push({ type: 'SAVE_WORKFLOW' });
200
- return { state: newState, effects };
201
- }
202
- export function computeNextAction(state) {
203
- const { phase, terminalStatus, config } = state;
204
- // If paused, suggest resume to implementing (or last phase)
205
- if (terminalStatus === 'paused') {
206
- return { action: 'implementing', feedback: state.rerunContext?.feedback };
207
- }
208
- // If failed, suggest retry
209
- if (terminalStatus === 'failed') {
210
- return { action: 'implementing', feedback: state.failureContext?.error };
211
- }
212
- switch (phase) {
213
- case 'draft':
214
- return { action: 'implementing' };
215
- case 'implementing':
216
- // In step-by-step mode, this phase means we're waiting for user to continue
217
- // The phase mapping already moved 'implemented' -> 'validating'
218
- // So if we're still in 'implementing', the execution is in progress
219
- return { action: 'none', reason: 'Implementation in progress' };
220
- case 'validating':
221
- // Validation phase: run the quality checks
222
- return { action: 'validating' };
223
- case 'approved':
224
- // Check if AI review is needed and not done yet
225
- if (config.aiCodeReview && !state.aiReviewResult) {
226
- return { action: 'ai-reviewing' };
227
- }
228
- // If human approval required, wait for user to click approve
229
- if (config.requireHumanApproval) {
230
- return { action: 'awaiting-approval' };
231
- }
232
- // Otherwise proceed to merge
233
- return { action: 'merging' };
234
- case 'merging':
235
- // Execute merge
236
- return { action: 'merging' };
237
- case 'done':
238
- return { action: 'none', reason: 'Workflow completed' };
239
- default:
240
- return { action: 'none', reason: 'Unknown phase' };
241
- }
242
- }
243
- // ============================================================================
244
- // HELPER FUNCTIONS
245
- // ============================================================================
246
- function incrementAttempts(state, phase) {
247
- const current = state.attempts[phase] ?? 0;
248
- return {
249
- ...state,
250
- attempts: {
251
- ...state.attempts,
252
- [phase]: current + 1,
253
- },
254
- };
255
- }
256
- function updateExecutionIds(state, phase, executionId) {
257
- const current = state.executionIds[phase] ?? [];
258
- return {
259
- ...state,
260
- executionIds: {
261
- ...state.executionIds,
262
- [phase]: [...current, executionId],
263
- },
264
- };
265
- }
266
- function getLastExecutionId(state, phase) {
267
- const ids = state.executionIds[phase] ?? [];
268
- return ids[ids.length - 1];
269
- }
270
- // ============================================================================
271
- // STATE FACTORY
272
- // Creates initial workflow state from task and config
273
- // ============================================================================
274
- export function createInitialState(workflowId, taskId, config) {
275
- const now = new Date().toISOString();
276
- return {
277
- id: workflowId,
278
- taskId,
279
- phase: 'draft',
280
- terminalStatus: null,
281
- startTime: now,
282
- lastUpdatedAt: now,
283
- attempts: {},
284
- executionIds: {},
285
- links: {},
286
- config,
287
- };
288
- }