vibeman 0.0.1 → 0.0.2

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 (71) hide show
  1. package/dist/index.js +5 -7
  2. package/dist/runtime/api/.tsbuildinfo +1 -1
  3. package/dist/runtime/api/agent/agent-service.d.ts +11 -13
  4. package/dist/runtime/api/agent/agent-service.js +25 -31
  5. package/dist/runtime/api/agent/ai-providers/claude-code-adapter.d.ts +2 -2
  6. package/dist/runtime/api/agent/ai-providers/claude-code-adapter.js +25 -36
  7. package/dist/runtime/api/agent/ai-providers/codex-cli-provider.js +48 -14
  8. package/dist/runtime/api/agent/ai-providers/types.d.ts +2 -0
  9. package/dist/runtime/api/agent/codex-cli-provider.test.js +37 -0
  10. package/dist/runtime/api/agent/parsers.d.ts +1 -0
  11. package/dist/runtime/api/agent/parsers.js +75 -8
  12. package/dist/runtime/api/agent/prompt-service.d.ts +14 -1
  13. package/dist/runtime/api/agent/prompt-service.js +123 -14
  14. package/dist/runtime/api/agent/prompt-service.test.d.ts +1 -0
  15. package/dist/runtime/api/agent/prompt-service.test.js +230 -0
  16. package/dist/runtime/api/agent/routing-policy.d.ts +14 -14
  17. package/dist/runtime/api/api/routers/ai.d.ts +6 -6
  18. package/dist/runtime/api/api/routers/ai.js +2 -17
  19. package/dist/runtime/api/api/routers/executions.d.ts +5 -5
  20. package/dist/runtime/api/api/routers/executions.js +12 -21
  21. package/dist/runtime/api/api/routers/provider-config.d.ts +165 -0
  22. package/dist/runtime/api/api/routers/provider-config.js +252 -0
  23. package/dist/runtime/api/api/routers/tasks.d.ts +10 -10
  24. package/dist/runtime/api/api/routers/workflows.d.ts +15 -16
  25. package/dist/runtime/api/api/routers/workflows.js +28 -26
  26. package/dist/runtime/api/api/routers/worktrees.d.ts +4 -5
  27. package/dist/runtime/api/api/routers/worktrees.js +11 -11
  28. package/dist/runtime/api/api/trpc.d.ts +18 -18
  29. package/dist/runtime/api/index.js +2 -10
  30. package/dist/runtime/api/lib/local-config.d.ts +245 -0
  31. package/dist/runtime/api/lib/local-config.js +288 -0
  32. package/dist/runtime/api/lib/provider-detection.d.ts +59 -0
  33. package/dist/runtime/api/lib/provider-detection.js +244 -0
  34. package/dist/runtime/api/lib/server/bootstrap.d.ts +38 -0
  35. package/dist/runtime/api/lib/server/bootstrap.js +197 -0
  36. package/dist/runtime/api/lib/server/project-root.js +24 -1
  37. package/dist/runtime/api/lib/trpc/server.d.ts +124 -31
  38. package/dist/runtime/api/lib/trpc/server.js +8 -8
  39. package/dist/runtime/api/lib/trpc/ws-server.js +2 -2
  40. package/dist/runtime/api/router.d.ts +125 -32
  41. package/dist/runtime/api/router.js +9 -31
  42. package/dist/runtime/api/settings-service.js +2 -0
  43. package/dist/runtime/api/workflows/vibing-orchestrator.d.ts +8 -3
  44. package/dist/runtime/api/workflows/vibing-orchestrator.js +182 -183
  45. package/dist/runtime/web/.next/BUILD_ID +1 -1
  46. package/dist/runtime/web/.next/app-build-manifest.json +2 -2
  47. package/dist/runtime/web/.next/build-manifest.json +2 -2
  48. package/dist/runtime/web/.next/prerender-manifest.json +3 -3
  49. package/dist/runtime/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  50. package/dist/runtime/web/.next/server/app/_not-found.html +2 -2
  51. package/dist/runtime/web/.next/server/app/_not-found.rsc +5 -5
  52. package/dist/runtime/web/.next/server/app/api/health/route.js +1 -1
  53. package/dist/runtime/web/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
  54. package/dist/runtime/web/.next/server/app/api/images/[...path]/route_client-reference-manifest.js +1 -1
  55. package/dist/runtime/web/.next/server/app/api/upload/route_client-reference-manifest.js +1 -1
  56. package/dist/runtime/web/.next/server/app/index.html +2 -2
  57. package/dist/runtime/web/.next/server/app/index.rsc +6 -6
  58. package/dist/runtime/web/.next/server/app/page.js +3 -3
  59. package/dist/runtime/web/.next/server/app/page_client-reference-manifest.js +1 -1
  60. package/dist/runtime/web/.next/server/chunks/458.js +1 -1
  61. package/dist/runtime/web/.next/server/pages/404.html +2 -2
  62. package/dist/runtime/web/.next/server/pages/500.html +1 -1
  63. package/dist/runtime/web/.next/server/pages-manifest.json +1 -1
  64. package/dist/runtime/web/.next/server/server-reference-manifest.json +1 -1
  65. package/dist/runtime/web/.next/static/chunks/app/{layout-dc0cfd29075b2160.js → layout-8435322f09fd0975.js} +1 -1
  66. package/dist/runtime/web/.next/static/chunks/app/page-8c3ba579efc6f918.js +1 -0
  67. package/dist/tsconfig.tsbuildinfo +1 -1
  68. package/package.json +5 -1
  69. package/dist/runtime/web/.next/static/chunks/app/page-f34a8b196b18850b.js +0 -1
  70. /package/dist/runtime/web/.next/static/{1HR8N0rJkCvFRtbTPJMyH → mRpNgPfbYR_0wrODzlg_4}/_buildManifest.js +0 -0
  71. /package/dist/runtime/web/.next/static/{1HR8N0rJkCvFRtbTPJMyH → mRpNgPfbYR_0wrODzlg_4}/_ssgManifest.js +0 -0
@@ -313,10 +313,11 @@ export class VibingOrchestrator extends EventEmitter {
313
313
  }
314
314
  return res;
315
315
  }
316
- async reviewCode(taskId, reviewContext, options) {
317
- const res = await this.agentService.reviewCode(taskId, reviewContext, {
318
- provider: options?.provider,
319
- model: options?.model,
316
+ async aiReviewCode(taskId, reviewContext, options) {
317
+ const res = await this.agentService.aiReviewCode(taskId, reviewContext, {
318
+ overrides: options?.overrides,
319
+ workflowId: options?.workflowId,
320
+ executionId: options?.executionId,
320
321
  });
321
322
  const wfId = options?.workflowId;
322
323
  if (wfId) {
@@ -338,17 +339,15 @@ export class VibingOrchestrator extends EventEmitter {
338
339
  return res;
339
340
  }
340
341
  async aiMerge(taskId, options) {
341
- const res = await this.agentService.aiMerge(taskId, { baseBranch: options?.baseBranch }, { provider: options?.provider, model: options?.model });
342
- if (options?.workflowId) {
343
- const wf = this.workflows.get(options.workflowId);
344
- if (wf) {
345
- wf.executionIds = wf.executionIds || {};
346
- const list = wf.executionIds['merging'] || [];
347
- wf.executionIds['merging'] = [...list, res.executionId];
348
- await this.saveWorkflow(wf);
349
- }
350
- }
351
- return res;
342
+ const overrides = options?.provider || options?.model
343
+ ? { provider: options?.provider, model: options?.model }
344
+ : undefined;
345
+ return await this.agentService.aiMerge(taskId, {
346
+ baseBranch: options?.baseBranch,
347
+ workflowId: options?.workflowId,
348
+ executionId: options?.executionId,
349
+ overrides,
350
+ });
352
351
  }
353
352
  // Worktree façade
354
353
  async listWorktrees() {
@@ -702,27 +701,7 @@ export class VibingOrchestrator extends EventEmitter {
702
701
  metrics.totalDurationMs = Object.values(metrics.durationsMs || {}).reduce((a, b) => a + (b || 0), 0);
703
702
  }
704
703
  workflow.lastUpdatedAt = now;
705
- // If entering awaiting-review, add a real timeline item so the UI
706
- // can expose review controls (instead of a placeholder that hides actions)
707
- if (newPhase === 'awaiting-review') {
708
- const hasReal = Array.isArray(workflow.timeline)
709
- ? workflow.timeline.some((t) => t.phase === 'awaiting-review' && !t.placeholder)
710
- : false;
711
- if (!hasReal) {
712
- workflow.timeline = this.buildTimeline(workflow, {
713
- id: `awaiting-review:${now}`,
714
- label: PHASE_LABELS['awaiting-review'],
715
- phase: 'awaiting-review',
716
- startTime: now,
717
- });
718
- }
719
- else {
720
- workflow.timeline = this.computeVisibleTimeline(workflow);
721
- }
722
- }
723
- else {
724
- workflow.timeline = this.computeVisibleTimeline(workflow);
725
- }
704
+ workflow.timeline = this.computeVisibleTimeline(workflow);
726
705
  log.info('Workflow phase transition', { workflowId, oldPhase, newPhase }, 'vibing-orchestrator');
727
706
  await this.saveWorkflow(workflow);
728
707
  this.emit('workflowPhaseChanged', { workflowId, oldPhase, newPhase, workflow });
@@ -773,6 +752,7 @@ export class VibingOrchestrator extends EventEmitter {
773
752
  }
774
753
  : undefined,
775
754
  providerOverride: providerOverride || workflow.metadata?.aiRoutingOverrides?.execute_task,
755
+ workflowConfig: workflow.metadata,
776
756
  })
777
757
  .catch((err) => log.error('Agent execution failed to start (implementation phase)', err, 'vibing-orchestrator'));
778
758
  const executionId = await createdPromise;
@@ -851,29 +831,26 @@ export class VibingOrchestrator extends EventEmitter {
851
831
  await this.executeImplementation(workflowId);
852
832
  return;
853
833
  case 'validating':
854
- await this.transitionToPhase(workflowId, 'validating');
855
834
  await this.executeValidation(workflowId);
856
835
  return;
857
836
  case 'ai-reviewing':
858
- await this.transitionToPhase(workflowId, 'ai-reviewing');
859
837
  await this.executeAiReviewPhase(workflowId);
860
838
  return;
839
+ case 'awaiting-review':
840
+ await this.executeAwaitingReview(workflowId);
841
+ return;
861
842
  case 'merging':
862
- await this.transitionToPhase(workflowId, 'merging');
863
843
  await this.executeMerge(workflowId);
864
844
  return;
865
845
  case 'approved':
866
846
  // Failure after approval typically means merge failed; try merging again
867
- await this.transitionToPhase(workflowId, 'approved');
868
847
  await this.executeMerge(workflowId);
869
848
  return;
870
849
  case 'cleaning':
871
- await this.transitionToPhase(workflowId, 'cleaning');
872
850
  await this.executeCleanup(workflowId);
873
851
  return;
874
852
  default:
875
853
  // For other phases, restart implementation as a safe default
876
- await this.transitionToPhase(workflowId, 'implementing');
877
854
  await this.executeImplementation(workflowId);
878
855
  return;
879
856
  }
@@ -944,20 +921,24 @@ export class VibingOrchestrator extends EventEmitter {
944
921
  /**
945
922
  * Perform AI code review of implemented changes
946
923
  */
947
- async performAICodeReview(workflowId) {
924
+ async performAICodeReview(workflowId, providedExecutionId) {
948
925
  const workflow = this.workflows.get(workflowId);
949
926
  if (!workflow)
950
- return false;
927
+ return { passed: false };
951
928
  try {
952
929
  log.info('Running AI code review', { taskId: workflow.taskId }, 'vibing-orchestrator');
953
930
  // Get worktree info for the task
954
931
  const worktree = this.agentService.getWorktreeInfo(workflow.taskId);
955
932
  if (!worktree) {
956
933
  log.warn('No worktree found for AI code review, skipping', undefined, 'vibing-orchestrator');
957
- return true; // do not fail if worktree missing
934
+ return { passed: true, executionId: providedExecutionId };
958
935
  }
959
936
  // Execute AI code review via agent service
960
- const review = await this.agentService.reviewCode(workflow.taskId, 'Automated AI code review triggered by workflow orchestrator', workflow.metadata?.aiRoutingOverrides?.ai_codereview);
937
+ const review = await this.agentService.aiReviewCode(workflow.taskId, 'Automated AI code review triggered by workflow orchestrator', {
938
+ overrides: workflow.metadata?.aiRoutingOverrides?.ai_codereview,
939
+ executionId: providedExecutionId,
940
+ workflowId,
941
+ });
961
942
  // Persist review result into workflow metadata
962
943
  const timestamp = new Date().toISOString();
963
944
  workflow.metadata = {
@@ -965,10 +946,13 @@ export class VibingOrchestrator extends EventEmitter {
965
946
  aiReviewResult: { ...review, timestamp },
966
947
  };
967
948
  // Track execution ID under ai-reviewing for observability
968
- if (review?.executionId) {
949
+ const executionId = providedExecutionId ?? review?.executionId;
950
+ if (executionId) {
969
951
  workflow.executionIds = workflow.executionIds || {};
970
952
  const list = workflow.executionIds['ai-reviewing'] || [];
971
- workflow.executionIds['ai-reviewing'] = [...list, review.executionId];
953
+ if (!list.includes(executionId)) {
954
+ workflow.executionIds['ai-reviewing'] = [...list, executionId];
955
+ }
972
956
  }
973
957
  await this.saveWorkflow(workflow);
974
958
  // Gate by minimum score from settings
@@ -977,10 +961,11 @@ export class VibingOrchestrator extends EventEmitter {
977
961
  const msg = `AI code review score too low (${review.qualityScore} < ${minScore})`;
978
962
  log.warn('AI code review score too low', { score: review.qualityScore, minScore }, 'vibing-orchestrator');
979
963
  workflow.error = msg;
980
- return false;
964
+ return { passed: false, executionId };
981
965
  }
982
966
  log.info('AI code review passed', { score: review.qualityScore }, 'vibing-orchestrator');
983
- return true;
967
+ workflow.error = undefined;
968
+ return { passed: true, executionId };
984
969
  }
985
970
  catch (error) {
986
971
  log.warn('AI code review failed', error, 'vibing-orchestrator');
@@ -989,7 +974,7 @@ export class VibingOrchestrator extends EventEmitter {
989
974
  if (workflowRef) {
990
975
  workflowRef.error = `AI code review failed: ${msg}`;
991
976
  }
992
- return false;
977
+ return { passed: false, executionId: providedExecutionId };
993
978
  }
994
979
  }
995
980
  /**
@@ -999,26 +984,42 @@ export class VibingOrchestrator extends EventEmitter {
999
984
  const workflow = this.workflows.get(workflowId);
1000
985
  if (!workflow)
1001
986
  return;
987
+ await this.transitionToPhase(workflowId, 'ai-reviewing');
1002
988
  try {
1003
- const before = workflow.executionIds?.['ai-reviewing']?.length || 0;
1004
- const passed = await this.performAICodeReview(workflowId);
1005
- // If an executionId was recorded for ai-reviewing, add instantaneous timeline entry
1006
- const after = workflow.executionIds?.['ai-reviewing']?.length || 0;
1007
- if (after > before) {
1008
- const execId = (workflow.executionIds?.['ai-reviewing'] || [])[after - 1];
1009
- workflow.timeline = this.buildTimeline(workflow, {
1010
- id: execId,
1011
- label: after > 1 ? `AI Review – Retry #${after}` : 'AI Review',
1012
- phase: 'ai-reviewing',
1013
- attempt: after,
1014
- executionId: execId,
1015
- startTime: new Date().toISOString(),
1016
- endTime: new Date().toISOString(),
1017
- reason: !passed
1018
- ? workflow.error || 'AI review did not pass the quality threshold'
1019
- : undefined,
1020
- });
1021
- await this.saveWorkflow(workflow);
989
+ const attemptList = workflow.executionIds?.['ai-reviewing'] || [];
990
+ const attempt = attemptList.length + 1;
991
+ const execId = generateId('review');
992
+ workflow.executionIds = workflow.executionIds || {};
993
+ const aiList = workflow.executionIds['ai-reviewing'] || [];
994
+ workflow.executionIds['ai-reviewing'] = [...aiList, execId];
995
+ workflow.timeline = this.buildTimeline(workflow, {
996
+ id: execId,
997
+ label: attempt > 1 ? `AI Review – Retry #${attempt}` : 'AI Review',
998
+ phase: 'ai-reviewing',
999
+ attempt,
1000
+ executionId: execId,
1001
+ startTime: new Date().toISOString(),
1002
+ });
1003
+ await this.saveWorkflow(workflow);
1004
+ this.emit('workflowExecutionStarted', {
1005
+ workflowId,
1006
+ executionId: execId,
1007
+ phase: 'ai-reviewing',
1008
+ workflow,
1009
+ });
1010
+ const { passed } = await this.performAICodeReview(workflowId, execId);
1011
+ const latest = this.workflows.get(workflowId);
1012
+ if (latest) {
1013
+ const timeline = (latest.timeline || []);
1014
+ const item = timeline.find((t) => t.executionId === execId);
1015
+ if (item) {
1016
+ item.endTime = new Date().toISOString();
1017
+ if (!passed) {
1018
+ item.reason = latest.error || 'AI review did not pass the quality threshold';
1019
+ }
1020
+ latest.timeline = this.computeVisibleTimeline(latest);
1021
+ await this.saveWorkflow(latest);
1022
+ }
1022
1023
  }
1023
1024
  if (!passed) {
1024
1025
  const reason = workflow.error || 'AI review did not pass the quality threshold';
@@ -1033,10 +1034,10 @@ export class VibingOrchestrator extends EventEmitter {
1033
1034
  workflow.lastUpdatedAt = new Date().toISOString();
1034
1035
  workflow.timeline = this.computeVisibleTimeline(workflow);
1035
1036
  await this.saveWorkflow(workflow);
1036
- await this.transitionToPhase(workflowId, 'awaiting-review');
1037
1037
  workflow.status = 'awaiting-review';
1038
1038
  workflow.lastUpdatedAt = new Date().toISOString();
1039
1039
  await this.saveWorkflow(workflow);
1040
+ await this.executeAwaitingReview(workflowId);
1040
1041
  const wf = this.workflows.get(workflowId);
1041
1042
  if (wf?.metadata.stepByStepMode) {
1042
1043
  log.info('Manual step mode – AI review passed; waiting for Continue', { workflowId }, 'vibing-orchestrator');
@@ -1046,6 +1047,25 @@ export class VibingOrchestrator extends EventEmitter {
1046
1047
  await this.handlePhaseFailure(workflowId, 'ai-reviewing', error);
1047
1048
  }
1048
1049
  }
1050
+ async executeAwaitingReview(workflowId) {
1051
+ const workflow = this.workflows.get(workflowId);
1052
+ if (!workflow)
1053
+ return;
1054
+ await this.transitionToPhase(workflowId, 'awaiting-review');
1055
+ const hasReal = Array.isArray(workflow.timeline)
1056
+ ? workflow.timeline.some((t) => t.phase === 'awaiting-review' && !t.placeholder)
1057
+ : false;
1058
+ if (!hasReal) {
1059
+ workflow.timeline = this.buildTimeline(workflow, {
1060
+ id: `awaiting-review:${new Date().toISOString()}`,
1061
+ label: PHASE_LABELS['awaiting-review'],
1062
+ phase: 'awaiting-review',
1063
+ startTime: new Date().toISOString(),
1064
+ });
1065
+ }
1066
+ workflow.timeline = this.computeVisibleTimeline(workflow);
1067
+ await this.saveWorkflow(workflow);
1068
+ }
1049
1069
  /**
1050
1070
  * Execute validation phase with quality checks
1051
1071
  */
@@ -1056,51 +1076,10 @@ export class VibingOrchestrator extends EventEmitter {
1056
1076
  await this.transitionToPhase(workflowId, 'validating');
1057
1077
  try {
1058
1078
  if (!workflow.metadata.autoQualityChecks) {
1059
- log.info('Skipping quality checks (disabled)', { taskId: workflow.taskId }, 'vibing-orchestrator');
1060
- // Create a generic execution for consistency/observability
1061
- const worktree = this.agentService.getWorktreeInfo(workflow.taskId);
1062
- const workingDirectory = worktree?.path || process.cwd();
1063
- const execId = await this.agentService.startGenericExecution(workflow.taskId, {
1064
- workflowId,
1065
- workingDirectory,
1066
- });
1067
- // Track execution id for validating phase and timeline
1068
- workflow.executionIds = workflow.executionIds || {};
1069
- const list = workflow.executionIds['validating'] || [];
1070
- workflow.executionIds['validating'] = [...list, execId];
1071
- workflow.timeline = this.buildTimeline(workflow, {
1072
- id: execId,
1073
- label: list.length >= 1 ? `Validation – Retry #${list.length + 1}` : 'Validation',
1074
- phase: 'validating',
1075
- attempt: list.length + 1,
1076
- executionId: execId,
1077
- startTime: new Date().toISOString(),
1078
- endTime: new Date().toISOString(),
1079
- reason: 'Auto quality checks disabled; skipping validation',
1080
- });
1081
- await this.agentService.logGenericExecution(execId, '[validation] Auto quality checks disabled; skipping validation.');
1082
- await this.agentService.completeGenericExecution(execId, 'completed');
1083
- workflow.timeline = this.computeVisibleTimeline(workflow);
1084
- await this.saveWorkflow(workflow);
1085
- // Mark checkpoint as validated even when skipping checks
1086
- workflow.status = 'validated';
1087
- workflow.lastUpdatedAt = new Date().toISOString();
1088
- await this.saveWorkflow(workflow);
1089
- await this.transitionToPhase(workflowId, 'awaiting-review');
1090
- const wf = this.workflows.get(workflowId);
1091
- if (wf?.metadata.stepByStepMode) {
1092
- log.info('Manual step mode – validation skipped; waiting for Continue (awaiting-review)', { workflowId }, 'vibing-orchestrator');
1093
- }
1079
+ // TODO implement later
1094
1080
  return;
1095
1081
  }
1096
1082
  log.info('Running quality checks', { taskId: workflow.taskId }, 'vibing-orchestrator');
1097
- // Timeline: start validation
1098
- workflow.timeline = this.buildTimeline(workflow, {
1099
- id: `validation:${new Date().toISOString()}`,
1100
- label: 'Validation',
1101
- phase: 'validating',
1102
- startTime: new Date().toISOString(),
1103
- });
1104
1083
  // Get worktree path for quality checks
1105
1084
  const worktree = this.agentService.getWorktreeInfo(workflow.taskId);
1106
1085
  const workingDirectory = worktree?.path || process.cwd();
@@ -1112,17 +1091,13 @@ export class VibingOrchestrator extends EventEmitter {
1112
1091
  workflow.executionIds = workflow.executionIds || {};
1113
1092
  const valList = workflow.executionIds['validating'] || [];
1114
1093
  workflow.executionIds['validating'] = [...valList, execId];
1115
- // Tie timeline item to this execution id
1116
- {
1117
- const timeline = workflow.timeline;
1118
- const tIdx = timeline.length - 1;
1119
- if (tIdx >= 0) {
1120
- const t = timeline[tIdx];
1121
- if (t && t.phase === 'validating' && !t.executionId)
1122
- t.executionId = execId;
1123
- }
1124
- }
1125
- workflow.timeline = this.computeVisibleTimeline(workflow);
1094
+ workflow.timeline = this.buildTimeline(workflow, {
1095
+ id: `validation:${new Date().toISOString()}`,
1096
+ label: 'Validation',
1097
+ phase: 'validating',
1098
+ startTime: new Date().toISOString(),
1099
+ executionId: execId,
1100
+ });
1126
1101
  await this.saveWorkflow(workflow);
1127
1102
  // Notify listeners that a validation execution has started
1128
1103
  this.emit('workflowExecutionStarted', {
@@ -1158,14 +1133,12 @@ export class VibingOrchestrator extends EventEmitter {
1158
1133
  });
1159
1134
  workflow.qualityResults = qualityResults;
1160
1135
  // Timeline: end validation
1161
- {
1162
- const timeline = workflow.timeline;
1163
- for (let i = timeline.length - 1; i >= 0; i--) {
1164
- const t = timeline[i];
1165
- if (t.phase === 'validating' && !t.endTime) {
1166
- t.endTime = new Date().toISOString();
1167
- break;
1168
- }
1136
+ const timeline = workflow.timeline;
1137
+ for (let i = timeline.length - 1; i >= 0; i--) {
1138
+ const t = timeline[i];
1139
+ if (t.phase === 'validating' && !t.endTime) {
1140
+ t.endTime = new Date().toISOString();
1141
+ break;
1169
1142
  }
1170
1143
  }
1171
1144
  // Mark execution complete (completed even if checks failed; workflow handles failure)
@@ -1184,37 +1157,17 @@ export class VibingOrchestrator extends EventEmitter {
1184
1157
  }
1185
1158
  log.info('Quality checks passed', { taskId: workflow.taskId }, 'vibing-orchestrator');
1186
1159
  // Update checkpoint status
1160
+ await this.transitionToPhase(workflowId, 'validated');
1187
1161
  workflow.status = 'validated';
1188
1162
  workflow.lastUpdatedAt = new Date().toISOString();
1189
1163
  await this.saveWorkflow(workflow);
1190
- // Move to AI review phase or directly to reviewing
1191
1164
  if (workflow.metadata.aiCodeReview) {
1192
- await this.transitionToPhase(workflowId, 'ai-reviewing');
1193
1165
  if (workflow.metadata.stepByStepMode) {
1194
1166
  log.info('Manual step mode – waiting for Continue to run AI review', { workflowId }, 'vibing-orchestrator');
1195
1167
  }
1196
1168
  else {
1197
1169
  await this.executeAiReviewPhase(workflowId);
1198
1170
  }
1199
- return;
1200
- }
1201
- await this.transitionToPhase(workflowId, 'validated');
1202
- // If manual step mode is off and human approval not required, auto-approve
1203
- if (!workflow.metadata.stepByStepMode && !workflow.metadata.requireHumanApproval) {
1204
- log.info('Auto-approving workflow (human approval disabled)', { workflowId }, 'vibing-orchestrator');
1205
- // Update checkpoint to approved
1206
- workflow.status = 'approved';
1207
- workflow.lastUpdatedAt = new Date().toISOString();
1208
- await this.saveWorkflow(workflow);
1209
- await this.approveWorkflow(workflowId);
1210
- }
1211
- else {
1212
- // Move into awaiting-review phase so UI shows review actions
1213
- await this.transitionToPhase(workflowId, 'awaiting-review');
1214
- workflow.status = 'awaiting-review';
1215
- workflow.lastUpdatedAt = new Date().toISOString();
1216
- await this.saveWorkflow(workflow);
1217
- this.emit('workflowReadyForReview', workflow);
1218
1171
  }
1219
1172
  }
1220
1173
  catch (error) {
@@ -1238,57 +1191,102 @@ export class VibingOrchestrator extends EventEmitter {
1238
1191
  if (!workflow)
1239
1192
  return;
1240
1193
  await this.transitionToPhase(workflowId, 'merging');
1194
+ let executionId;
1241
1195
  try {
1242
1196
  log.info('AI merging changes', { taskId: workflow.taskId }, 'vibing-orchestrator');
1243
- // AI-assisted merge to avoid raw git conflicts
1244
- const { executionId } = await this.agentService.aiMerge(workflow.taskId, { baseBranch: 'main' }, providerOverride || workflow.metadata?.aiRoutingOverrides?.ai_merge);
1197
+ const mergeList = workflow.executionIds?.['merging'] || [];
1198
+ const attempt = mergeList.length + 1;
1199
+ executionId = generateId('merge');
1200
+ const startTime = new Date().toISOString();
1245
1201
  workflow.executionIds = workflow.executionIds || {};
1246
- const mergeList = workflow.executionIds['merging'] || [];
1247
1202
  workflow.executionIds['merging'] = [...mergeList, executionId];
1248
- // Timeline: record merge attempt start
1249
1203
  workflow.timeline = this.buildTimeline(workflow, {
1250
1204
  id: executionId,
1251
- label: mergeList.length >= 1 ? `Merge – Retry #${mergeList.length + 1}` : 'Merge',
1205
+ label: attempt > 1 ? `Merge – Retry #${attempt}` : 'Merge',
1252
1206
  phase: 'merging',
1253
- attempt: mergeList.length + 1,
1207
+ attempt,
1254
1208
  executionId,
1255
- startTime: new Date().toISOString(),
1209
+ startTime,
1256
1210
  });
1211
+ workflow.lastUpdatedAt = startTime;
1257
1212
  await this.saveWorkflow(workflow);
1258
- // Notify listeners that a merge execution for this workflow has started
1213
+ this.emit('workflowUpdated', workflow);
1259
1214
  this.emit('workflowExecutionStarted', {
1260
1215
  workflowId,
1261
1216
  executionId,
1262
1217
  phase: 'merging',
1263
1218
  workflow,
1264
1219
  });
1265
- // Wait for AI merge to complete
1266
- await this.waitForExecution(executionId, 15 * 60 * 1000);
1220
+ await this.agentService.aiMerge(workflow.taskId, {
1221
+ baseBranch: 'main',
1222
+ executionId,
1223
+ workflowId,
1224
+ overrides: providerOverride || workflow.metadata?.aiRoutingOverrides?.ai_merge,
1225
+ });
1267
1226
  const mergeExec = this.agentService.getExecutionStatus(executionId);
1268
1227
  if (!mergeExec || mergeExec.status !== 'completed') {
1269
1228
  throw new Error(`AI merge failed: ${mergeExec?.error || 'Unknown error'}`);
1270
1229
  }
1271
- // Timeline: complete merge attempt
1272
- const tMerge = workflow.timeline.find((t) => t.executionId === executionId);
1230
+ const latest = this.workflows.get(workflowId) || workflow;
1231
+ const timeline = (latest.timeline || []);
1232
+ const tMerge = timeline.find((t) => t.executionId === executionId);
1273
1233
  if (tMerge) {
1274
1234
  tMerge.endTime = mergeExec.endTime || new Date().toISOString();
1275
1235
  tMerge.usage = mergeExec.usage;
1276
- workflow.timeline = this.computeVisibleTimeline(workflow);
1277
- await this.saveWorkflow(workflow);
1236
+ latest.timeline = this.computeVisibleTimeline(latest);
1237
+ latest.lastUpdatedAt = new Date().toISOString();
1238
+ await this.saveWorkflow(latest);
1278
1239
  }
1279
- // Update task status to done (after merge)
1280
1240
  await this.taskService.updateTask(workflow.taskId, { status: 'done' });
1281
- // Move to cleanup phase; final completion is after cleanup
1282
1241
  await this.transitionToPhase(workflowId, 'merged');
1283
- workflow.status = 'merged';
1284
- workflow.lastUpdatedAt = new Date().toISOString();
1285
- await this.saveWorkflow(workflow);
1242
+ const mergedWorkflow = this.workflows.get(workflowId);
1243
+ if (mergedWorkflow) {
1244
+ mergedWorkflow.status = 'merged';
1245
+ mergedWorkflow.lastUpdatedAt = new Date().toISOString();
1246
+ await this.saveWorkflow(mergedWorkflow);
1247
+ }
1248
+ if (workflow.metadata.stepByStepMode) {
1249
+ log.info('Manual step mode – merge completed; waiting for Continue', { workflowId }, 'vibing-orchestrator');
1250
+ }
1251
+ else {
1252
+ await this.updateCleanupStatus(workflowId);
1253
+ }
1286
1254
  log.info('Merge completed, ready for cleanup', { taskId: workflow.taskId }, 'vibing-orchestrator');
1287
1255
  }
1288
1256
  catch (error) {
1257
+ const latest = this.workflows.get(workflowId) || workflow;
1258
+ if (executionId && latest) {
1259
+ const timeline = (latest.timeline || []);
1260
+ const pending = timeline.find((t) => t.executionId === executionId);
1261
+ if (pending && !pending.endTime) {
1262
+ pending.endTime = new Date().toISOString();
1263
+ if (!pending.reason) {
1264
+ pending.reason = error instanceof Error ? error.message : String(error);
1265
+ }
1266
+ latest.timeline = this.computeVisibleTimeline(latest);
1267
+ latest.lastUpdatedAt = new Date().toISOString();
1268
+ await this.saveWorkflow(latest);
1269
+ }
1270
+ }
1289
1271
  await this.handlePhaseFailure(workflowId, 'merging', error);
1290
1272
  }
1291
1273
  }
1274
+ async updateCleanupStatus(workflowId) {
1275
+ const workflow = this.workflows.get(workflowId);
1276
+ if (!workflow)
1277
+ return;
1278
+ await this.transitionToPhase(workflowId, 'cleaning');
1279
+ workflow.status = 'cleaning';
1280
+ workflow.lastUpdatedAt = new Date().toISOString();
1281
+ workflow.timeline = this.buildTimeline(workflow, {
1282
+ id: `cleaning:${new Date().toISOString()}`,
1283
+ label: 'Cleaning',
1284
+ phase: 'cleaning',
1285
+ startTime: new Date().toISOString(),
1286
+ });
1287
+ await this.saveWorkflow(workflow);
1288
+ this.emit('workflowUpdated', workflow);
1289
+ }
1292
1290
  /**
1293
1291
  * Public: run merge phase on demand
1294
1292
  * This will attempt merge regardless of current phase.
@@ -1297,10 +1295,7 @@ export class VibingOrchestrator extends EventEmitter {
1297
1295
  const workflow = this.workflows.get(workflowId);
1298
1296
  if (!workflow)
1299
1297
  throw new Error(`Workflow ${workflowId} not found`);
1300
- await this.transitionToPhase(workflowId, 'merging');
1301
- // If not already approved/reviewing, proceed anyway to merge attempt
1302
- await this.executeMerge(workflowId, options);
1303
- await this.transitionToPhase(workflowId, 'merged');
1298
+ await this.executeMerge(workflowId, { provider: options?.provider, model: options?.model });
1304
1299
  }
1305
1300
  /**
1306
1301
  * Execute cleanup phase: remove worktree/branch and finalize workflow
@@ -1434,11 +1429,18 @@ export class VibingOrchestrator extends EventEmitter {
1434
1429
  */
1435
1430
  async waitForExecution(executionId, timeoutMs = 30 * 60 * 1000) {
1436
1431
  const startTime = Date.now();
1432
+ let missingStatusWarned = false;
1437
1433
  while (Date.now() - startTime < timeoutMs) {
1438
1434
  const execution = this.agentService.getExecutionStatus(executionId);
1439
1435
  if (!execution) {
1440
- throw new Error(`Execution ${executionId} not found`);
1436
+ if (!missingStatusWarned) {
1437
+ log.debug('Execution status not yet available; waiting for agent to register', { executionId }, 'vibing-orchestrator');
1438
+ missingStatusWarned = true;
1439
+ }
1440
+ await new Promise((resolve) => setTimeout(resolve, 500));
1441
+ continue;
1441
1442
  }
1443
+ missingStatusWarned = false;
1442
1444
  if (['completed', 'failed', 'cancelled'].includes(execution.status)) {
1443
1445
  return;
1444
1446
  }
@@ -1623,7 +1625,6 @@ export class VibingOrchestrator extends EventEmitter {
1623
1625
  await this.rerunImplementation(workflowId);
1624
1626
  }
1625
1627
  else {
1626
- await this.transitionToPhase(workflowId, 'implementing');
1627
1628
  await this.executeImplementation(workflowId);
1628
1629
  }
1629
1630
  return;
@@ -1636,23 +1637,21 @@ export class VibingOrchestrator extends EventEmitter {
1636
1637
  }
1637
1638
  switch (next) {
1638
1639
  case 'implementing':
1639
- await this.transitionToPhase(workflowId, 'implementing');
1640
1640
  await this.executeImplementation(workflowId);
1641
1641
  return;
1642
1642
  case 'validating':
1643
- await this.transitionToPhase(workflowId, 'validating');
1644
1643
  await this.executeValidation(workflowId);
1645
1644
  return;
1646
1645
  case 'ai-reviewing':
1647
- await this.transitionToPhase(workflowId, 'ai-reviewing');
1648
1646
  await this.executeAiReviewPhase(workflowId);
1649
1647
  return;
1648
+ case 'awaiting-review':
1649
+ await this.executeAwaitingReview(workflowId);
1650
+ return;
1650
1651
  case 'merging':
1651
- await this.transitionToPhase(workflowId, 'merging');
1652
1652
  await this.executeMerge(workflowId);
1653
1653
  return;
1654
1654
  case 'cleaning':
1655
- await this.transitionToPhase(workflowId, 'cleaning');
1656
1655
  await this.executeCleanup(workflowId);
1657
1656
  return;
1658
1657
  default:
@@ -1 +1 @@
1
- 1HR8N0rJkCvFRtbTPJMyH
1
+ mRpNgPfbYR_0wrODzlg_4
@@ -32,7 +32,7 @@
32
32
  "static/chunks/cac567b0-5b77dd12911823cd.js",
33
33
  "static/chunks/575-e2478287c27da87b.js",
34
34
  "static/chunks/765-e838910065b50c3d.js",
35
- "static/chunks/app/layout-dc0cfd29075b2160.js"
35
+ "static/chunks/app/layout-8435322f09fd0975.js"
36
36
  ],
37
37
  "/api/upload/route": [
38
38
  "static/chunks/webpack-c8de37305b4635cf.js",
@@ -53,7 +53,7 @@
53
53
  "static/chunks/8bb4d8db-3e2aa02b0a2384b9.js",
54
54
  "static/chunks/575-e2478287c27da87b.js",
55
55
  "static/chunks/277-0142a939f08738c3.js",
56
- "static/chunks/app/page-f34a8b196b18850b.js"
56
+ "static/chunks/app/page-8c3ba579efc6f918.js"
57
57
  ]
58
58
  }
59
59
  }
@@ -5,8 +5,8 @@
5
5
  "devFiles": [],
6
6
  "ampDevFiles": [],
7
7
  "lowPriorityFiles": [
8
- "static/1HR8N0rJkCvFRtbTPJMyH/_buildManifest.js",
9
- "static/1HR8N0rJkCvFRtbTPJMyH/_ssgManifest.js"
8
+ "static/mRpNgPfbYR_0wrODzlg_4/_buildManifest.js",
9
+ "static/mRpNgPfbYR_0wrODzlg_4/_ssgManifest.js"
10
10
  ],
11
11
  "rootMainFiles": [
12
12
  "static/chunks/webpack-c8de37305b4635cf.js",
@@ -54,8 +54,8 @@
54
54
  "dynamicRoutes": {},
55
55
  "notFoundRoutes": [],
56
56
  "preview": {
57
- "previewModeId": "8e2b4700b3835cefe001ca613df2b7cf",
58
- "previewModeSigningKey": "4b6ab31ca7f677b0810b510c825dd99259abfecd0395c780a478db5ac5ae6bbe",
59
- "previewModeEncryptionKey": "526c2a6ca7d03f4b8729f55f155a88b42439a67c50f4a2d88f748e12d0bcd7b0"
57
+ "previewModeId": "03a9b538fba39124f8691ffda05da8fa",
58
+ "previewModeSigningKey": "7af5b8d11e26e2288ce29c591de1c6dfb0afa5d616b2b87cdff307f8c516c426",
59
+ "previewModeEncryptionKey": "a19958cfa9716d3f3eab68594cebb7e760a891f19851604a0378b92c3f1985d9"
60
60
  }
61
61
  }