vibeman 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +5 -7
- package/dist/runtime/api/.tsbuildinfo +1 -1
- package/dist/runtime/api/agent/agent-service.d.ts +18 -19
- package/dist/runtime/api/agent/agent-service.js +61 -58
- package/dist/runtime/api/agent/ai-providers/claude-code-adapter.d.ts +2 -2
- package/dist/runtime/api/agent/ai-providers/claude-code-adapter.js +25 -36
- package/dist/runtime/api/agent/ai-providers/codex-cli-provider.d.ts +2 -0
- package/dist/runtime/api/agent/ai-providers/codex-cli-provider.js +109 -43
- package/dist/runtime/api/agent/ai-providers/types.d.ts +2 -0
- package/dist/runtime/api/agent/codex-cli-provider.test.js +83 -1
- package/dist/runtime/api/agent/parsers.d.ts +1 -0
- package/dist/runtime/api/agent/parsers.js +75 -8
- package/dist/runtime/api/agent/prompt-service.d.ts +14 -1
- package/dist/runtime/api/agent/prompt-service.js +123 -14
- package/dist/runtime/api/agent/prompt-service.test.js +230 -0
- package/dist/runtime/api/agent/routing-policy.d.ts +25 -42
- package/dist/runtime/api/agent/routing-policy.js +82 -132
- package/dist/runtime/api/agent/routing-policy.test.js +63 -0
- package/dist/runtime/api/api/routers/ai.d.ts +19 -7
- package/dist/runtime/api/api/routers/ai.js +9 -23
- package/dist/runtime/api/api/routers/executions.d.ts +4 -4
- package/dist/runtime/api/api/routers/executions.js +12 -21
- package/dist/runtime/api/api/routers/provider-config.d.ts +165 -0
- package/dist/runtime/api/api/routers/provider-config.js +252 -0
- package/dist/runtime/api/api/routers/tasks.d.ts +9 -9
- package/dist/runtime/api/api/routers/workflows.d.ts +23 -16
- package/dist/runtime/api/api/routers/workflows.js +30 -27
- package/dist/runtime/api/api/routers/worktrees.d.ts +4 -5
- package/dist/runtime/api/api/routers/worktrees.js +11 -11
- package/dist/runtime/api/api/trpc.d.ts +16 -16
- package/dist/runtime/api/index.js +2 -10
- package/dist/runtime/api/lib/local-config.d.ts +245 -0
- package/dist/runtime/api/lib/local-config.js +288 -0
- package/dist/runtime/api/lib/provider-detection.d.ts +59 -0
- package/dist/runtime/api/lib/provider-detection.js +244 -0
- package/dist/runtime/api/lib/server/bootstrap.d.ts +38 -0
- package/dist/runtime/api/lib/server/bootstrap.js +197 -0
- package/dist/runtime/api/lib/server/project-root.js +24 -1
- package/dist/runtime/api/lib/trpc/server.d.ts +143 -30
- package/dist/runtime/api/lib/trpc/server.js +8 -8
- package/dist/runtime/api/lib/trpc/ws-server.js +2 -2
- package/dist/runtime/api/router.d.ts +144 -31
- package/dist/runtime/api/router.js +9 -31
- package/dist/runtime/api/settings-service.js +51 -1
- package/dist/runtime/api/types/index.d.ts +8 -1
- package/dist/runtime/api/types/settings.d.ts +15 -2
- package/dist/runtime/api/workflows/vibing-orchestrator.d.ts +8 -3
- package/dist/runtime/api/workflows/vibing-orchestrator.js +214 -184
- package/dist/runtime/web/.next/BUILD_ID +1 -1
- package/dist/runtime/web/.next/app-build-manifest.json +19 -12
- package/dist/runtime/web/.next/app-path-routes-manifest.json +2 -1
- package/dist/runtime/web/.next/build-manifest.json +2 -2
- package/dist/runtime/web/.next/prerender-manifest.json +10 -10
- package/dist/runtime/web/.next/routes-manifest.json +8 -0
- package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route.js +1 -0
- package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route.js.nft.json +1 -0
- package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route_client-reference-manifest.js +1 -0
- package/dist/runtime/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/runtime/web/.next/server/app/_not-found.html +2 -2
- package/dist/runtime/web/.next/server/app/_not-found.rsc +5 -5
- package/dist/runtime/web/.next/server/app/api/health/route.js +1 -1
- package/dist/runtime/web/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
- package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js +1 -1
- package/dist/runtime/web/.next/server/app/api/images/[...path]/route_client-reference-manifest.js +1 -1
- package/dist/runtime/web/.next/server/app/api/upload/route.js +1 -1
- package/dist/runtime/web/.next/server/app/api/upload/route_client-reference-manifest.js +1 -1
- package/dist/runtime/web/.next/server/app/index.html +2 -2
- package/dist/runtime/web/.next/server/app/index.rsc +6 -6
- package/dist/runtime/web/.next/server/app/page.js +21 -21
- package/dist/runtime/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/runtime/web/.next/server/app-paths-manifest.json +2 -1
- package/dist/runtime/web/.next/server/chunks/458.js +1 -1
- package/dist/runtime/web/.next/server/pages/404.html +2 -2
- package/dist/runtime/web/.next/server/pages/500.html +1 -1
- package/dist/runtime/web/.next/server/pages-manifest.json +1 -1
- package/dist/runtime/web/.next/server/server-reference-manifest.json +1 -1
- package/dist/runtime/web/.next/static/5_15u1WQCxN1_eHZpldCv/_buildManifest.js +1 -0
- package/dist/runtime/web/.next/static/chunks/{277-0142a939f08738c3.js → 823-6f371a6e829adbba.js} +1 -1
- package/dist/runtime/web/.next/static/chunks/app/.vibeman/assets/images/[...path]/route-751c9265a65409e5.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/api/health/route-751c9265a65409e5.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/api/images/[...path]/route-751c9265a65409e5.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/api/upload/route-751c9265a65409e5.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/{layout-dc0cfd29075b2160.js → layout-8435322f09fd0975.js} +1 -1
- package/dist/runtime/web/.next/static/chunks/app/page-9fe7d75095b4ccec.js +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -1
- package/dist/runtime/api/lib/image-paste-drop-extension.d.ts +0 -26
- package/dist/runtime/api/lib/image-paste-drop-extension.js +0 -125
- package/dist/runtime/api/lib/markdown-utils.d.ts +0 -8
- package/dist/runtime/api/lib/markdown-utils.js +0 -282
- package/dist/runtime/api/lib/markdown-utils.test.js +0 -348
- package/dist/runtime/api/lib/tiptap-utils.clamp-selection.test.js +0 -27
- package/dist/runtime/api/lib/tiptap-utils.d.ts +0 -130
- package/dist/runtime/api/lib/tiptap-utils.js +0 -327
- package/dist/runtime/web/.next/static/1HR8N0rJkCvFRtbTPJMyH/_buildManifest.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/api/health/route-105a61ae865ba536.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/api/images/[...path]/route-105a61ae865ba536.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/api/upload/route-105a61ae865ba536.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/page-f34a8b196b18850b.js +0 -1
- /package/dist/runtime/api/{lib/markdown-utils.test.d.ts → agent/prompt-service.test.d.ts} +0 -0
- /package/dist/runtime/api/{lib/tiptap-utils.clamp-selection.test.d.ts → agent/routing-policy.test.d.ts} +0 -0
- /package/dist/runtime/web/.next/static/{1HR8N0rJkCvFRtbTPJMyH → 5_15u1WQCxN1_eHZpldCv}/_ssgManifest.js +0 -0
|
@@ -313,19 +313,22 @@ export class VibingOrchestrator extends EventEmitter {
|
|
|
313
313
|
}
|
|
314
314
|
return res;
|
|
315
315
|
}
|
|
316
|
-
async
|
|
317
|
-
const res = await this.agentService.
|
|
318
|
-
|
|
319
|
-
|
|
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) {
|
|
323
324
|
const wf = this.workflows.get(wfId);
|
|
324
325
|
if (wf) {
|
|
326
|
+
const timestamp = new Date().toISOString();
|
|
325
327
|
wf.metadata = {
|
|
326
328
|
...wf.metadata,
|
|
327
|
-
aiReviewResult: { ...res, timestamp
|
|
329
|
+
aiReviewResult: { ...res, timestamp },
|
|
328
330
|
};
|
|
331
|
+
wf.aiReviewResult = { ...res, timestamp };
|
|
329
332
|
// Record execution ID under ai-reviewing for UI/observability
|
|
330
333
|
if (res?.executionId) {
|
|
331
334
|
wf.executionIds = wf.executionIds || {};
|
|
@@ -338,17 +341,15 @@ export class VibingOrchestrator extends EventEmitter {
|
|
|
338
341
|
return res;
|
|
339
342
|
}
|
|
340
343
|
async aiMerge(taskId, options) {
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
}
|
|
351
|
-
return res;
|
|
344
|
+
const overrides = options?.provider || options?.model
|
|
345
|
+
? { provider: options?.provider, model: options?.model }
|
|
346
|
+
: undefined;
|
|
347
|
+
return await this.agentService.aiMerge(taskId, {
|
|
348
|
+
baseBranch: options?.baseBranch,
|
|
349
|
+
workflowId: options?.workflowId,
|
|
350
|
+
executionId: options?.executionId,
|
|
351
|
+
overrides,
|
|
352
|
+
});
|
|
352
353
|
}
|
|
353
354
|
// Worktree façade
|
|
354
355
|
async listWorktrees() {
|
|
@@ -702,27 +703,7 @@ export class VibingOrchestrator extends EventEmitter {
|
|
|
702
703
|
metrics.totalDurationMs = Object.values(metrics.durationsMs || {}).reduce((a, b) => a + (b || 0), 0);
|
|
703
704
|
}
|
|
704
705
|
workflow.lastUpdatedAt = now;
|
|
705
|
-
|
|
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
|
-
}
|
|
706
|
+
workflow.timeline = this.computeVisibleTimeline(workflow);
|
|
726
707
|
log.info('Workflow phase transition', { workflowId, oldPhase, newPhase }, 'vibing-orchestrator');
|
|
727
708
|
await this.saveWorkflow(workflow);
|
|
728
709
|
this.emit('workflowPhaseChanged', { workflowId, oldPhase, newPhase, workflow });
|
|
@@ -758,6 +739,14 @@ export class VibingOrchestrator extends EventEmitter {
|
|
|
758
739
|
(workflow.failureContext?.atPhase
|
|
759
740
|
? `Retry after ${workflow.failureContext.atPhase} failure`
|
|
760
741
|
: undefined);
|
|
742
|
+
const aiReviewSource = workflow.aiReviewResult || workflow.metadata?.aiReviewResult;
|
|
743
|
+
const aiReviewContext = aiReviewSource
|
|
744
|
+
? {
|
|
745
|
+
summary: aiReviewSource.reviewSummary,
|
|
746
|
+
recommendations: aiReviewSource.recommendations,
|
|
747
|
+
score: aiReviewSource.qualityScore,
|
|
748
|
+
}
|
|
749
|
+
: undefined;
|
|
761
750
|
// Prepare to capture executionId from event, then start execution
|
|
762
751
|
const createdPromise = this.waitForExecutionCreated(workflow.taskId, workflowId, 20000);
|
|
763
752
|
// Fire-and-forget; attach catch to prevent unhandled rejection from crashing process
|
|
@@ -769,10 +758,12 @@ export class VibingOrchestrator extends EventEmitter {
|
|
|
769
758
|
previousExecutionId: previousExecId,
|
|
770
759
|
failurePhase: workflow.failureContext?.atPhase,
|
|
771
760
|
qualityResults: workflow.qualityResults,
|
|
761
|
+
aiReview: aiReviewContext,
|
|
772
762
|
attempt,
|
|
773
763
|
}
|
|
774
764
|
: undefined,
|
|
775
765
|
providerOverride: providerOverride || workflow.metadata?.aiRoutingOverrides?.execute_task,
|
|
766
|
+
workflowConfig: workflow.metadata,
|
|
776
767
|
})
|
|
777
768
|
.catch((err) => log.error('Agent execution failed to start (implementation phase)', err, 'vibing-orchestrator'));
|
|
778
769
|
const executionId = await createdPromise;
|
|
@@ -851,29 +842,26 @@ export class VibingOrchestrator extends EventEmitter {
|
|
|
851
842
|
await this.executeImplementation(workflowId);
|
|
852
843
|
return;
|
|
853
844
|
case 'validating':
|
|
854
|
-
await this.transitionToPhase(workflowId, 'validating');
|
|
855
845
|
await this.executeValidation(workflowId);
|
|
856
846
|
return;
|
|
857
847
|
case 'ai-reviewing':
|
|
858
|
-
await this.transitionToPhase(workflowId, 'ai-reviewing');
|
|
859
848
|
await this.executeAiReviewPhase(workflowId);
|
|
860
849
|
return;
|
|
850
|
+
case 'awaiting-review':
|
|
851
|
+
await this.executeAwaitingReview(workflowId);
|
|
852
|
+
return;
|
|
861
853
|
case 'merging':
|
|
862
|
-
await this.transitionToPhase(workflowId, 'merging');
|
|
863
854
|
await this.executeMerge(workflowId);
|
|
864
855
|
return;
|
|
865
856
|
case 'approved':
|
|
866
857
|
// Failure after approval typically means merge failed; try merging again
|
|
867
|
-
await this.transitionToPhase(workflowId, 'approved');
|
|
868
858
|
await this.executeMerge(workflowId);
|
|
869
859
|
return;
|
|
870
860
|
case 'cleaning':
|
|
871
|
-
await this.transitionToPhase(workflowId, 'cleaning');
|
|
872
861
|
await this.executeCleanup(workflowId);
|
|
873
862
|
return;
|
|
874
863
|
default:
|
|
875
864
|
// For other phases, restart implementation as a safe default
|
|
876
|
-
await this.transitionToPhase(workflowId, 'implementing');
|
|
877
865
|
await this.executeImplementation(workflowId);
|
|
878
866
|
return;
|
|
879
867
|
}
|
|
@@ -944,31 +932,39 @@ export class VibingOrchestrator extends EventEmitter {
|
|
|
944
932
|
/**
|
|
945
933
|
* Perform AI code review of implemented changes
|
|
946
934
|
*/
|
|
947
|
-
async performAICodeReview(workflowId) {
|
|
935
|
+
async performAICodeReview(workflowId, providedExecutionId) {
|
|
948
936
|
const workflow = this.workflows.get(workflowId);
|
|
949
937
|
if (!workflow)
|
|
950
|
-
return false;
|
|
938
|
+
return { passed: false };
|
|
951
939
|
try {
|
|
952
940
|
log.info('Running AI code review', { taskId: workflow.taskId }, 'vibing-orchestrator');
|
|
953
941
|
// Get worktree info for the task
|
|
954
942
|
const worktree = this.agentService.getWorktreeInfo(workflow.taskId);
|
|
955
943
|
if (!worktree) {
|
|
956
944
|
log.warn('No worktree found for AI code review, skipping', undefined, 'vibing-orchestrator');
|
|
957
|
-
return
|
|
945
|
+
return { passed: true, executionId: providedExecutionId };
|
|
958
946
|
}
|
|
959
947
|
// Execute AI code review via agent service
|
|
960
|
-
const review = await this.agentService.
|
|
948
|
+
const review = await this.agentService.aiReviewCode(workflow.taskId, 'Automated AI code review triggered by workflow orchestrator', {
|
|
949
|
+
overrides: workflow.metadata?.aiRoutingOverrides?.ai_codereview,
|
|
950
|
+
executionId: providedExecutionId,
|
|
951
|
+
workflowId,
|
|
952
|
+
});
|
|
961
953
|
// Persist review result into workflow metadata
|
|
962
954
|
const timestamp = new Date().toISOString();
|
|
963
955
|
workflow.metadata = {
|
|
964
956
|
...workflow.metadata,
|
|
965
957
|
aiReviewResult: { ...review, timestamp },
|
|
966
958
|
};
|
|
959
|
+
workflow.aiReviewResult = { ...review, timestamp };
|
|
967
960
|
// Track execution ID under ai-reviewing for observability
|
|
968
|
-
|
|
961
|
+
const executionId = providedExecutionId ?? review?.executionId;
|
|
962
|
+
if (executionId) {
|
|
969
963
|
workflow.executionIds = workflow.executionIds || {};
|
|
970
964
|
const list = workflow.executionIds['ai-reviewing'] || [];
|
|
971
|
-
|
|
965
|
+
if (!list.includes(executionId)) {
|
|
966
|
+
workflow.executionIds['ai-reviewing'] = [...list, executionId];
|
|
967
|
+
}
|
|
972
968
|
}
|
|
973
969
|
await this.saveWorkflow(workflow);
|
|
974
970
|
// Gate by minimum score from settings
|
|
@@ -977,10 +973,11 @@ export class VibingOrchestrator extends EventEmitter {
|
|
|
977
973
|
const msg = `AI code review score too low (${review.qualityScore} < ${minScore})`;
|
|
978
974
|
log.warn('AI code review score too low', { score: review.qualityScore, minScore }, 'vibing-orchestrator');
|
|
979
975
|
workflow.error = msg;
|
|
980
|
-
return false;
|
|
976
|
+
return { passed: false, executionId };
|
|
981
977
|
}
|
|
982
978
|
log.info('AI code review passed', { score: review.qualityScore }, 'vibing-orchestrator');
|
|
983
|
-
|
|
979
|
+
workflow.error = undefined;
|
|
980
|
+
return { passed: true, executionId };
|
|
984
981
|
}
|
|
985
982
|
catch (error) {
|
|
986
983
|
log.warn('AI code review failed', error, 'vibing-orchestrator');
|
|
@@ -989,7 +986,7 @@ export class VibingOrchestrator extends EventEmitter {
|
|
|
989
986
|
if (workflowRef) {
|
|
990
987
|
workflowRef.error = `AI code review failed: ${msg}`;
|
|
991
988
|
}
|
|
992
|
-
return false;
|
|
989
|
+
return { passed: false, executionId: providedExecutionId };
|
|
993
990
|
}
|
|
994
991
|
}
|
|
995
992
|
/**
|
|
@@ -999,26 +996,42 @@ export class VibingOrchestrator extends EventEmitter {
|
|
|
999
996
|
const workflow = this.workflows.get(workflowId);
|
|
1000
997
|
if (!workflow)
|
|
1001
998
|
return;
|
|
999
|
+
await this.transitionToPhase(workflowId, 'ai-reviewing');
|
|
1002
1000
|
try {
|
|
1003
|
-
const
|
|
1004
|
-
const
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1001
|
+
const attemptList = workflow.executionIds?.['ai-reviewing'] || [];
|
|
1002
|
+
const attempt = attemptList.length + 1;
|
|
1003
|
+
const execId = generateId('review');
|
|
1004
|
+
workflow.executionIds = workflow.executionIds || {};
|
|
1005
|
+
const aiList = workflow.executionIds['ai-reviewing'] || [];
|
|
1006
|
+
workflow.executionIds['ai-reviewing'] = [...aiList, execId];
|
|
1007
|
+
workflow.timeline = this.buildTimeline(workflow, {
|
|
1008
|
+
id: execId,
|
|
1009
|
+
label: attempt > 1 ? `AI Review – Retry #${attempt}` : 'AI Review',
|
|
1010
|
+
phase: 'ai-reviewing',
|
|
1011
|
+
attempt,
|
|
1012
|
+
executionId: execId,
|
|
1013
|
+
startTime: new Date().toISOString(),
|
|
1014
|
+
});
|
|
1015
|
+
await this.saveWorkflow(workflow);
|
|
1016
|
+
this.emit('workflowExecutionStarted', {
|
|
1017
|
+
workflowId,
|
|
1018
|
+
executionId: execId,
|
|
1019
|
+
phase: 'ai-reviewing',
|
|
1020
|
+
workflow,
|
|
1021
|
+
});
|
|
1022
|
+
const { passed } = await this.performAICodeReview(workflowId, execId);
|
|
1023
|
+
const latest = this.workflows.get(workflowId);
|
|
1024
|
+
if (latest) {
|
|
1025
|
+
const timeline = (latest.timeline || []);
|
|
1026
|
+
const item = timeline.find((t) => t.executionId === execId);
|
|
1027
|
+
if (item) {
|
|
1028
|
+
item.endTime = new Date().toISOString();
|
|
1029
|
+
if (!passed) {
|
|
1030
|
+
item.reason = latest.error || 'AI review did not pass the quality threshold';
|
|
1031
|
+
}
|
|
1032
|
+
latest.timeline = this.computeVisibleTimeline(latest);
|
|
1033
|
+
await this.saveWorkflow(latest);
|
|
1034
|
+
}
|
|
1022
1035
|
}
|
|
1023
1036
|
if (!passed) {
|
|
1024
1037
|
const reason = workflow.error || 'AI review did not pass the quality threshold';
|
|
@@ -1033,10 +1046,10 @@ export class VibingOrchestrator extends EventEmitter {
|
|
|
1033
1046
|
workflow.lastUpdatedAt = new Date().toISOString();
|
|
1034
1047
|
workflow.timeline = this.computeVisibleTimeline(workflow);
|
|
1035
1048
|
await this.saveWorkflow(workflow);
|
|
1036
|
-
await this.transitionToPhase(workflowId, 'awaiting-review');
|
|
1037
1049
|
workflow.status = 'awaiting-review';
|
|
1038
1050
|
workflow.lastUpdatedAt = new Date().toISOString();
|
|
1039
1051
|
await this.saveWorkflow(workflow);
|
|
1052
|
+
await this.executeAwaitingReview(workflowId);
|
|
1040
1053
|
const wf = this.workflows.get(workflowId);
|
|
1041
1054
|
if (wf?.metadata.stepByStepMode) {
|
|
1042
1055
|
log.info('Manual step mode – AI review passed; waiting for Continue', { workflowId }, 'vibing-orchestrator');
|
|
@@ -1046,6 +1059,25 @@ export class VibingOrchestrator extends EventEmitter {
|
|
|
1046
1059
|
await this.handlePhaseFailure(workflowId, 'ai-reviewing', error);
|
|
1047
1060
|
}
|
|
1048
1061
|
}
|
|
1062
|
+
async executeAwaitingReview(workflowId) {
|
|
1063
|
+
const workflow = this.workflows.get(workflowId);
|
|
1064
|
+
if (!workflow)
|
|
1065
|
+
return;
|
|
1066
|
+
await this.transitionToPhase(workflowId, 'awaiting-review');
|
|
1067
|
+
const hasReal = Array.isArray(workflow.timeline)
|
|
1068
|
+
? workflow.timeline.some((t) => t.phase === 'awaiting-review' && !t.placeholder)
|
|
1069
|
+
: false;
|
|
1070
|
+
if (!hasReal) {
|
|
1071
|
+
workflow.timeline = this.buildTimeline(workflow, {
|
|
1072
|
+
id: `awaiting-review:${new Date().toISOString()}`,
|
|
1073
|
+
label: PHASE_LABELS['awaiting-review'],
|
|
1074
|
+
phase: 'awaiting-review',
|
|
1075
|
+
startTime: new Date().toISOString(),
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
workflow.timeline = this.computeVisibleTimeline(workflow);
|
|
1079
|
+
await this.saveWorkflow(workflow);
|
|
1080
|
+
}
|
|
1049
1081
|
/**
|
|
1050
1082
|
* Execute validation phase with quality checks
|
|
1051
1083
|
*/
|
|
@@ -1056,51 +1088,10 @@ export class VibingOrchestrator extends EventEmitter {
|
|
|
1056
1088
|
await this.transitionToPhase(workflowId, 'validating');
|
|
1057
1089
|
try {
|
|
1058
1090
|
if (!workflow.metadata.autoQualityChecks) {
|
|
1059
|
-
|
|
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
|
-
}
|
|
1091
|
+
// TODO implement later
|
|
1094
1092
|
return;
|
|
1095
1093
|
}
|
|
1096
1094
|
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
1095
|
// Get worktree path for quality checks
|
|
1105
1096
|
const worktree = this.agentService.getWorktreeInfo(workflow.taskId);
|
|
1106
1097
|
const workingDirectory = worktree?.path || process.cwd();
|
|
@@ -1109,20 +1100,27 @@ export class VibingOrchestrator extends EventEmitter {
|
|
|
1109
1100
|
workflowId,
|
|
1110
1101
|
workingDirectory,
|
|
1111
1102
|
});
|
|
1103
|
+
const qualityOverride = workflow.metadata?.aiRoutingOverrides?.quality_checks;
|
|
1104
|
+
try {
|
|
1105
|
+
const resolved = await this.agentService.previewProviderForOperation('quality_checks', qualityOverride);
|
|
1106
|
+
const providerLabel = resolved.model
|
|
1107
|
+
? `${resolved.provider} (model: ${resolved.model})`
|
|
1108
|
+
: resolved.provider;
|
|
1109
|
+
await this.agentService.logGenericExecution(execId, `[validation] Using provider: ${providerLabel}`);
|
|
1110
|
+
}
|
|
1111
|
+
catch (err) {
|
|
1112
|
+
log.warn('Failed to resolve quality-check provider', err, 'vibing-orchestrator');
|
|
1113
|
+
}
|
|
1112
1114
|
workflow.executionIds = workflow.executionIds || {};
|
|
1113
1115
|
const valList = workflow.executionIds['validating'] || [];
|
|
1114
1116
|
workflow.executionIds['validating'] = [...valList, execId];
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
t.executionId = execId;
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
workflow.timeline = this.computeVisibleTimeline(workflow);
|
|
1117
|
+
workflow.timeline = this.buildTimeline(workflow, {
|
|
1118
|
+
id: `validation:${new Date().toISOString()}`,
|
|
1119
|
+
label: 'Validation',
|
|
1120
|
+
phase: 'validating',
|
|
1121
|
+
startTime: new Date().toISOString(),
|
|
1122
|
+
executionId: execId,
|
|
1123
|
+
});
|
|
1126
1124
|
await this.saveWorkflow(workflow);
|
|
1127
1125
|
// Notify listeners that a validation execution has started
|
|
1128
1126
|
this.emit('workflowExecutionStarted', {
|
|
@@ -1158,14 +1156,12 @@ export class VibingOrchestrator extends EventEmitter {
|
|
|
1158
1156
|
});
|
|
1159
1157
|
workflow.qualityResults = qualityResults;
|
|
1160
1158
|
// Timeline: end validation
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
break;
|
|
1168
|
-
}
|
|
1159
|
+
const timeline = workflow.timeline;
|
|
1160
|
+
for (let i = timeline.length - 1; i >= 0; i--) {
|
|
1161
|
+
const t = timeline[i];
|
|
1162
|
+
if (t.phase === 'validating' && !t.endTime) {
|
|
1163
|
+
t.endTime = new Date().toISOString();
|
|
1164
|
+
break;
|
|
1169
1165
|
}
|
|
1170
1166
|
}
|
|
1171
1167
|
// Mark execution complete (completed even if checks failed; workflow handles failure)
|
|
@@ -1184,37 +1180,17 @@ export class VibingOrchestrator extends EventEmitter {
|
|
|
1184
1180
|
}
|
|
1185
1181
|
log.info('Quality checks passed', { taskId: workflow.taskId }, 'vibing-orchestrator');
|
|
1186
1182
|
// Update checkpoint status
|
|
1183
|
+
await this.transitionToPhase(workflowId, 'validated');
|
|
1187
1184
|
workflow.status = 'validated';
|
|
1188
1185
|
workflow.lastUpdatedAt = new Date().toISOString();
|
|
1189
1186
|
await this.saveWorkflow(workflow);
|
|
1190
|
-
// Move to AI review phase or directly to reviewing
|
|
1191
1187
|
if (workflow.metadata.aiCodeReview) {
|
|
1192
|
-
await this.transitionToPhase(workflowId, 'ai-reviewing');
|
|
1193
1188
|
if (workflow.metadata.stepByStepMode) {
|
|
1194
1189
|
log.info('Manual step mode – waiting for Continue to run AI review', { workflowId }, 'vibing-orchestrator');
|
|
1195
1190
|
}
|
|
1196
1191
|
else {
|
|
1197
1192
|
await this.executeAiReviewPhase(workflowId);
|
|
1198
1193
|
}
|
|
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
1194
|
}
|
|
1219
1195
|
}
|
|
1220
1196
|
catch (error) {
|
|
@@ -1238,57 +1214,102 @@ export class VibingOrchestrator extends EventEmitter {
|
|
|
1238
1214
|
if (!workflow)
|
|
1239
1215
|
return;
|
|
1240
1216
|
await this.transitionToPhase(workflowId, 'merging');
|
|
1217
|
+
let executionId;
|
|
1241
1218
|
try {
|
|
1242
1219
|
log.info('AI merging changes', { taskId: workflow.taskId }, 'vibing-orchestrator');
|
|
1243
|
-
|
|
1244
|
-
const
|
|
1220
|
+
const mergeList = workflow.executionIds?.['merging'] || [];
|
|
1221
|
+
const attempt = mergeList.length + 1;
|
|
1222
|
+
executionId = generateId('merge');
|
|
1223
|
+
const startTime = new Date().toISOString();
|
|
1245
1224
|
workflow.executionIds = workflow.executionIds || {};
|
|
1246
|
-
const mergeList = workflow.executionIds['merging'] || [];
|
|
1247
1225
|
workflow.executionIds['merging'] = [...mergeList, executionId];
|
|
1248
|
-
// Timeline: record merge attempt start
|
|
1249
1226
|
workflow.timeline = this.buildTimeline(workflow, {
|
|
1250
1227
|
id: executionId,
|
|
1251
|
-
label:
|
|
1228
|
+
label: attempt > 1 ? `Merge – Retry #${attempt}` : 'Merge',
|
|
1252
1229
|
phase: 'merging',
|
|
1253
|
-
attempt
|
|
1230
|
+
attempt,
|
|
1254
1231
|
executionId,
|
|
1255
|
-
startTime
|
|
1232
|
+
startTime,
|
|
1256
1233
|
});
|
|
1234
|
+
workflow.lastUpdatedAt = startTime;
|
|
1257
1235
|
await this.saveWorkflow(workflow);
|
|
1258
|
-
|
|
1236
|
+
this.emit('workflowUpdated', workflow);
|
|
1259
1237
|
this.emit('workflowExecutionStarted', {
|
|
1260
1238
|
workflowId,
|
|
1261
1239
|
executionId,
|
|
1262
1240
|
phase: 'merging',
|
|
1263
1241
|
workflow,
|
|
1264
1242
|
});
|
|
1265
|
-
|
|
1266
|
-
|
|
1243
|
+
await this.agentService.aiMerge(workflow.taskId, {
|
|
1244
|
+
baseBranch: 'main',
|
|
1245
|
+
executionId,
|
|
1246
|
+
workflowId,
|
|
1247
|
+
overrides: providerOverride || workflow.metadata?.aiRoutingOverrides?.ai_merge,
|
|
1248
|
+
});
|
|
1267
1249
|
const mergeExec = this.agentService.getExecutionStatus(executionId);
|
|
1268
1250
|
if (!mergeExec || mergeExec.status !== 'completed') {
|
|
1269
1251
|
throw new Error(`AI merge failed: ${mergeExec?.error || 'Unknown error'}`);
|
|
1270
1252
|
}
|
|
1271
|
-
|
|
1272
|
-
const
|
|
1253
|
+
const latest = this.workflows.get(workflowId) || workflow;
|
|
1254
|
+
const timeline = (latest.timeline || []);
|
|
1255
|
+
const tMerge = timeline.find((t) => t.executionId === executionId);
|
|
1273
1256
|
if (tMerge) {
|
|
1274
1257
|
tMerge.endTime = mergeExec.endTime || new Date().toISOString();
|
|
1275
1258
|
tMerge.usage = mergeExec.usage;
|
|
1276
|
-
|
|
1277
|
-
|
|
1259
|
+
latest.timeline = this.computeVisibleTimeline(latest);
|
|
1260
|
+
latest.lastUpdatedAt = new Date().toISOString();
|
|
1261
|
+
await this.saveWorkflow(latest);
|
|
1278
1262
|
}
|
|
1279
|
-
// Update task status to done (after merge)
|
|
1280
1263
|
await this.taskService.updateTask(workflow.taskId, { status: 'done' });
|
|
1281
|
-
// Move to cleanup phase; final completion is after cleanup
|
|
1282
1264
|
await this.transitionToPhase(workflowId, 'merged');
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1265
|
+
const mergedWorkflow = this.workflows.get(workflowId);
|
|
1266
|
+
if (mergedWorkflow) {
|
|
1267
|
+
mergedWorkflow.status = 'merged';
|
|
1268
|
+
mergedWorkflow.lastUpdatedAt = new Date().toISOString();
|
|
1269
|
+
await this.saveWorkflow(mergedWorkflow);
|
|
1270
|
+
}
|
|
1271
|
+
if (workflow.metadata.stepByStepMode) {
|
|
1272
|
+
log.info('Manual step mode – merge completed; waiting for Continue', { workflowId }, 'vibing-orchestrator');
|
|
1273
|
+
}
|
|
1274
|
+
else {
|
|
1275
|
+
await this.updateCleanupStatus(workflowId);
|
|
1276
|
+
}
|
|
1286
1277
|
log.info('Merge completed, ready for cleanup', { taskId: workflow.taskId }, 'vibing-orchestrator');
|
|
1287
1278
|
}
|
|
1288
1279
|
catch (error) {
|
|
1280
|
+
const latest = this.workflows.get(workflowId) || workflow;
|
|
1281
|
+
if (executionId && latest) {
|
|
1282
|
+
const timeline = (latest.timeline || []);
|
|
1283
|
+
const pending = timeline.find((t) => t.executionId === executionId);
|
|
1284
|
+
if (pending && !pending.endTime) {
|
|
1285
|
+
pending.endTime = new Date().toISOString();
|
|
1286
|
+
if (!pending.reason) {
|
|
1287
|
+
pending.reason = error instanceof Error ? error.message : String(error);
|
|
1288
|
+
}
|
|
1289
|
+
latest.timeline = this.computeVisibleTimeline(latest);
|
|
1290
|
+
latest.lastUpdatedAt = new Date().toISOString();
|
|
1291
|
+
await this.saveWorkflow(latest);
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1289
1294
|
await this.handlePhaseFailure(workflowId, 'merging', error);
|
|
1290
1295
|
}
|
|
1291
1296
|
}
|
|
1297
|
+
async updateCleanupStatus(workflowId) {
|
|
1298
|
+
const workflow = this.workflows.get(workflowId);
|
|
1299
|
+
if (!workflow)
|
|
1300
|
+
return;
|
|
1301
|
+
await this.transitionToPhase(workflowId, 'cleaning');
|
|
1302
|
+
workflow.status = 'cleaning';
|
|
1303
|
+
workflow.lastUpdatedAt = new Date().toISOString();
|
|
1304
|
+
workflow.timeline = this.buildTimeline(workflow, {
|
|
1305
|
+
id: `cleaning:${new Date().toISOString()}`,
|
|
1306
|
+
label: 'Cleaning',
|
|
1307
|
+
phase: 'cleaning',
|
|
1308
|
+
startTime: new Date().toISOString(),
|
|
1309
|
+
});
|
|
1310
|
+
await this.saveWorkflow(workflow);
|
|
1311
|
+
this.emit('workflowUpdated', workflow);
|
|
1312
|
+
}
|
|
1292
1313
|
/**
|
|
1293
1314
|
* Public: run merge phase on demand
|
|
1294
1315
|
* This will attempt merge regardless of current phase.
|
|
@@ -1297,10 +1318,7 @@ export class VibingOrchestrator extends EventEmitter {
|
|
|
1297
1318
|
const workflow = this.workflows.get(workflowId);
|
|
1298
1319
|
if (!workflow)
|
|
1299
1320
|
throw new Error(`Workflow ${workflowId} not found`);
|
|
1300
|
-
await this.
|
|
1301
|
-
// If not already approved/reviewing, proceed anyway to merge attempt
|
|
1302
|
-
await this.executeMerge(workflowId, options);
|
|
1303
|
-
await this.transitionToPhase(workflowId, 'merged');
|
|
1321
|
+
await this.executeMerge(workflowId, { provider: options?.provider, model: options?.model });
|
|
1304
1322
|
}
|
|
1305
1323
|
/**
|
|
1306
1324
|
* Execute cleanup phase: remove worktree/branch and finalize workflow
|
|
@@ -1434,11 +1452,18 @@ export class VibingOrchestrator extends EventEmitter {
|
|
|
1434
1452
|
*/
|
|
1435
1453
|
async waitForExecution(executionId, timeoutMs = 30 * 60 * 1000) {
|
|
1436
1454
|
const startTime = Date.now();
|
|
1455
|
+
let missingStatusWarned = false;
|
|
1437
1456
|
while (Date.now() - startTime < timeoutMs) {
|
|
1438
1457
|
const execution = this.agentService.getExecutionStatus(executionId);
|
|
1439
1458
|
if (!execution) {
|
|
1440
|
-
|
|
1459
|
+
if (!missingStatusWarned) {
|
|
1460
|
+
log.debug('Execution status not yet available; waiting for agent to register', { executionId }, 'vibing-orchestrator');
|
|
1461
|
+
missingStatusWarned = true;
|
|
1462
|
+
}
|
|
1463
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1464
|
+
continue;
|
|
1441
1465
|
}
|
|
1466
|
+
missingStatusWarned = false;
|
|
1442
1467
|
if (['completed', 'failed', 'cancelled'].includes(execution.status)) {
|
|
1443
1468
|
return;
|
|
1444
1469
|
}
|
|
@@ -1623,7 +1648,6 @@ export class VibingOrchestrator extends EventEmitter {
|
|
|
1623
1648
|
await this.rerunImplementation(workflowId);
|
|
1624
1649
|
}
|
|
1625
1650
|
else {
|
|
1626
|
-
await this.transitionToPhase(workflowId, 'implementing');
|
|
1627
1651
|
await this.executeImplementation(workflowId);
|
|
1628
1652
|
}
|
|
1629
1653
|
return;
|
|
@@ -1636,23 +1660,21 @@ export class VibingOrchestrator extends EventEmitter {
|
|
|
1636
1660
|
}
|
|
1637
1661
|
switch (next) {
|
|
1638
1662
|
case 'implementing':
|
|
1639
|
-
await this.transitionToPhase(workflowId, 'implementing');
|
|
1640
1663
|
await this.executeImplementation(workflowId);
|
|
1641
1664
|
return;
|
|
1642
1665
|
case 'validating':
|
|
1643
|
-
await this.transitionToPhase(workflowId, 'validating');
|
|
1644
1666
|
await this.executeValidation(workflowId);
|
|
1645
1667
|
return;
|
|
1646
1668
|
case 'ai-reviewing':
|
|
1647
|
-
await this.transitionToPhase(workflowId, 'ai-reviewing');
|
|
1648
1669
|
await this.executeAiReviewPhase(workflowId);
|
|
1649
1670
|
return;
|
|
1671
|
+
case 'awaiting-review':
|
|
1672
|
+
await this.executeAwaitingReview(workflowId);
|
|
1673
|
+
return;
|
|
1650
1674
|
case 'merging':
|
|
1651
|
-
await this.transitionToPhase(workflowId, 'merging');
|
|
1652
1675
|
await this.executeMerge(workflowId);
|
|
1653
1676
|
return;
|
|
1654
1677
|
case 'cleaning':
|
|
1655
|
-
await this.transitionToPhase(workflowId, 'cleaning');
|
|
1656
1678
|
await this.executeCleanup(workflowId);
|
|
1657
1679
|
return;
|
|
1658
1680
|
default:
|
|
@@ -1832,6 +1854,14 @@ function serializeVibe(workflow) {
|
|
|
1832
1854
|
timestamp: new Date(workflow.qualityResults.timestamp).toISOString(),
|
|
1833
1855
|
}
|
|
1834
1856
|
: undefined,
|
|
1857
|
+
aiReviewResult: workflow.aiReviewResult
|
|
1858
|
+
? {
|
|
1859
|
+
...workflow.aiReviewResult,
|
|
1860
|
+
timestamp: workflow.aiReviewResult.timestamp
|
|
1861
|
+
? new Date(workflow.aiReviewResult.timestamp).toISOString()
|
|
1862
|
+
: undefined,
|
|
1863
|
+
}
|
|
1864
|
+
: undefined,
|
|
1835
1865
|
};
|
|
1836
1866
|
}
|
|
1837
1867
|
async function getDbStats(db) {
|