steroids-cli 0.8.33 → 0.8.34
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/commands/loop-phases.d.ts +6 -2
- package/dist/commands/loop-phases.d.ts.map +1 -1
- package/dist/commands/loop-phases.js +42 -2
- package/dist/commands/loop-phases.js.map +1 -1
- package/dist/commands/merge.d.ts.map +1 -1
- package/dist/commands/merge.js +13 -1
- package/dist/commands/merge.js.map +1 -1
- package/dist/commands/runners-parallel.d.ts +1 -0
- package/dist/commands/runners-parallel.d.ts.map +1 -1
- package/dist/commands/runners-parallel.js +93 -6
- package/dist/commands/runners-parallel.js.map +1 -1
- package/dist/commands/workspaces.d.ts.map +1 -1
- package/dist/commands/workspaces.js +38 -4
- package/dist/commands/workspaces.js.map +1 -1
- package/dist/config/loader.d.ts +9 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +9 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +45 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/database/queries.d.ts.map +1 -1
- package/dist/database/queries.js +15 -1
- package/dist/database/queries.js.map +1 -1
- package/dist/database/schema.d.ts +2 -2
- package/dist/database/schema.d.ts.map +1 -1
- package/dist/database/schema.js +9 -0
- package/dist/database/schema.js.map +1 -1
- package/dist/orchestrator/coder.js +1 -1
- package/dist/orchestrator/coder.js.map +1 -1
- package/dist/orchestrator/coordinator.d.ts.map +1 -1
- package/dist/orchestrator/coordinator.js +5 -3
- package/dist/orchestrator/coordinator.js.map +1 -1
- package/dist/orchestrator/fallback-handler.d.ts +2 -1
- package/dist/orchestrator/fallback-handler.d.ts.map +1 -1
- package/dist/orchestrator/fallback-handler.js +36 -20
- package/dist/orchestrator/fallback-handler.js.map +1 -1
- package/dist/orchestrator/post-reviewer.d.ts.map +1 -1
- package/dist/orchestrator/post-reviewer.js +18 -23
- package/dist/orchestrator/post-reviewer.js.map +1 -1
- package/dist/orchestrator/reviewer.d.ts +1 -1
- package/dist/orchestrator/reviewer.d.ts.map +1 -1
- package/dist/orchestrator/reviewer.js +19 -16
- package/dist/orchestrator/reviewer.js.map +1 -1
- package/dist/parallel/clone.d.ts +13 -0
- package/dist/parallel/clone.d.ts.map +1 -1
- package/dist/parallel/clone.js +40 -0
- package/dist/parallel/clone.js.map +1 -1
- package/dist/parallel/merge-conflict-attempts.d.ts +8 -0
- package/dist/parallel/merge-conflict-attempts.d.ts.map +1 -0
- package/dist/parallel/merge-conflict-attempts.js +68 -0
- package/dist/parallel/merge-conflict-attempts.js.map +1 -0
- package/dist/parallel/merge-conflict-invoke.d.ts +2 -0
- package/dist/parallel/merge-conflict-invoke.d.ts.map +1 -0
- package/dist/parallel/merge-conflict-invoke.js +41 -0
- package/dist/parallel/merge-conflict-invoke.js.map +1 -0
- package/dist/parallel/merge-conflict-prompts.d.ts +23 -0
- package/dist/parallel/merge-conflict-prompts.d.ts.map +1 -0
- package/dist/parallel/merge-conflict-prompts.js +40 -0
- package/dist/parallel/merge-conflict-prompts.js.map +1 -0
- package/dist/parallel/merge-conflict-task.d.ts +3 -0
- package/dist/parallel/merge-conflict-task.d.ts.map +1 -0
- package/dist/parallel/merge-conflict-task.js +59 -0
- package/dist/parallel/merge-conflict-task.js.map +1 -0
- package/dist/parallel/merge-conflict.d.ts +7 -7
- package/dist/parallel/merge-conflict.d.ts.map +1 -1
- package/dist/parallel/merge-conflict.js +91 -135
- package/dist/parallel/merge-conflict.js.map +1 -1
- package/dist/parallel/merge-errors.d.ts +2 -0
- package/dist/parallel/merge-errors.d.ts.map +1 -1
- package/dist/parallel/merge-errors.js +2 -0
- package/dist/parallel/merge-errors.js.map +1 -1
- package/dist/parallel/merge-git.d.ts.map +1 -1
- package/dist/parallel/merge-git.js +4 -1
- package/dist/parallel/merge-git.js.map +1 -1
- package/dist/parallel/merge-lock.d.ts +4 -2
- package/dist/parallel/merge-lock.d.ts.map +1 -1
- package/dist/parallel/merge-lock.js +51 -18
- package/dist/parallel/merge-lock.js.map +1 -1
- package/dist/parallel/merge-progress.d.ts +3 -1
- package/dist/parallel/merge-progress.d.ts.map +1 -1
- package/dist/parallel/merge-progress.js +9 -4
- package/dist/parallel/merge-progress.js.map +1 -1
- package/dist/parallel/merge.d.ts +4 -0
- package/dist/parallel/merge.d.ts.map +1 -1
- package/dist/parallel/merge.js +222 -17
- package/dist/parallel/merge.js.map +1 -1
- package/dist/prompts/prompt-helpers.d.ts +1 -1
- package/dist/prompts/prompt-helpers.d.ts.map +1 -1
- package/dist/prompts/prompt-helpers.js +10 -7
- package/dist/prompts/prompt-helpers.js.map +1 -1
- package/dist/prompts/reviewer.d.ts.map +1 -1
- package/dist/prompts/reviewer.js +48 -21
- package/dist/prompts/reviewer.js.map +1 -1
- package/dist/runners/global-db.d.ts +32 -0
- package/dist/runners/global-db.d.ts.map +1 -1
- package/dist/runners/global-db.js +403 -2
- package/dist/runners/global-db.js.map +1 -1
- package/dist/runners/orchestrator-loop.d.ts.map +1 -1
- package/dist/runners/orchestrator-loop.js +50 -3
- package/dist/runners/orchestrator-loop.js.map +1 -1
- package/dist/runners/wakeup.d.ts +1 -0
- package/dist/runners/wakeup.d.ts.map +1 -1
- package/dist/runners/wakeup.js +363 -29
- package/dist/runners/wakeup.js.map +1 -1
- package/migrations/012_add_merge_lock_epoch.sql +15 -0
- package/migrations/013_add_merge_progress_applied_commit_sha.sql +15 -0
- package/migrations/manifest.json +17 -1
- package/package.json +1 -1
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MAX_CONFLICT_ATTEMPTS = void 0;
|
|
4
|
+
exports.recordConflictAttempt = recordConflictAttempt;
|
|
5
|
+
exports.clearConflictAttemptState = clearConflictAttemptState;
|
|
6
|
+
const global_db_js_1 = require("../runners/global-db.js");
|
|
7
|
+
const merge_errors_js_1 = require("./merge-errors.js");
|
|
8
|
+
exports.MAX_CONFLICT_ATTEMPTS = 5;
|
|
9
|
+
const MAX_CONFLICT_BACKOFF_MINUTES = 30;
|
|
10
|
+
function recordConflictAttempt(sessionId, workstreamId) {
|
|
11
|
+
const { db, close } = (0, global_db_js_1.openGlobalDatabase)();
|
|
12
|
+
try {
|
|
13
|
+
const row = db
|
|
14
|
+
.prepare(`SELECT conflict_attempts
|
|
15
|
+
FROM workstreams
|
|
16
|
+
WHERE session_id = ?
|
|
17
|
+
AND id = ?
|
|
18
|
+
LIMIT 1`)
|
|
19
|
+
.get(sessionId, workstreamId);
|
|
20
|
+
if (!row) {
|
|
21
|
+
throw new merge_errors_js_1.ParallelMergeError('Parallel workstream row not found while recording conflict attempt', 'LEASE_ROW_MISSING');
|
|
22
|
+
}
|
|
23
|
+
const attempts = (row.conflict_attempts ?? 0) + 1;
|
|
24
|
+
if (attempts >= exports.MAX_CONFLICT_ATTEMPTS) {
|
|
25
|
+
db.prepare(`UPDATE workstreams
|
|
26
|
+
SET conflict_attempts = ?,
|
|
27
|
+
status = 'failed',
|
|
28
|
+
next_retry_at = NULL,
|
|
29
|
+
last_reconcile_action = 'blocked_conflict',
|
|
30
|
+
last_reconciled_at = datetime('now')
|
|
31
|
+
WHERE session_id = ?
|
|
32
|
+
AND id = ?`).run(attempts, sessionId, workstreamId);
|
|
33
|
+
db.prepare(`UPDATE parallel_sessions
|
|
34
|
+
SET status = 'blocked_conflict',
|
|
35
|
+
completed_at = NULL
|
|
36
|
+
WHERE id = ?`).run(sessionId);
|
|
37
|
+
return { attempts, blocked: true, backoffMinutes: null };
|
|
38
|
+
}
|
|
39
|
+
const backoffMinutes = Math.min(2 ** Math.max(0, attempts - 1), MAX_CONFLICT_BACKOFF_MINUTES);
|
|
40
|
+
db.prepare(`UPDATE workstreams
|
|
41
|
+
SET conflict_attempts = ?,
|
|
42
|
+
next_retry_at = datetime('now', ?),
|
|
43
|
+
last_reconcile_action = 'conflict_retry',
|
|
44
|
+
last_reconciled_at = datetime('now')
|
|
45
|
+
WHERE session_id = ?
|
|
46
|
+
AND id = ?`).run(attempts, `+${backoffMinutes} minutes`, sessionId, workstreamId);
|
|
47
|
+
return { attempts, blocked: false, backoffMinutes };
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
close();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function clearConflictAttemptState(sessionId, workstreamId) {
|
|
54
|
+
const { db, close } = (0, global_db_js_1.openGlobalDatabase)();
|
|
55
|
+
try {
|
|
56
|
+
db.prepare(`UPDATE workstreams
|
|
57
|
+
SET conflict_attempts = 0,
|
|
58
|
+
next_retry_at = NULL,
|
|
59
|
+
last_reconcile_action = 'conflict_resolved',
|
|
60
|
+
last_reconciled_at = datetime('now')
|
|
61
|
+
WHERE session_id = ?
|
|
62
|
+
AND id = ?`).run(sessionId, workstreamId);
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
close();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=merge-conflict-attempts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-conflict-attempts.js","sourceRoot":"","sources":["../../src/parallel/merge-conflict-attempts.ts"],"names":[],"mappings":";;;AAMA,sDA6DC;AAED,8DAeC;AApFD,0DAA6D;AAC7D,uDAAuD;AAE1C,QAAA,qBAAqB,GAAG,CAAC,CAAC;AACvC,MAAM,4BAA4B,GAAG,EAAE,CAAC;AAExC,SAAgB,qBAAqB,CACnC,SAAiB,EACjB,YAAoB;IAEpB,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,IAAA,iCAAkB,GAAE,CAAC;IAC3C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE;aACX,OAAO,CACN;;;;iBAIS,CACV;aACA,GAAG,CAAC,SAAS,EAAE,YAAY,CAA8C,CAAC;QAE7E,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,oCAAkB,CAC1B,oEAAoE,EACpE,mBAAmB,CACpB,CAAC;QACJ,CAAC;QAED,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAC,iBAAiB,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QAClD,IAAI,QAAQ,IAAI,6BAAqB,EAAE,CAAC;YACtC,EAAE,CAAC,OAAO,CACR;;;;;;;sBAOc,CACf,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;YAEzC,EAAE,CAAC,OAAO,CACR;;;sBAGc,CACf,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAEjB,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;QAC3D,CAAC;QAED,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,GAAG,CAAC,CAAC,EAAE,4BAA4B,CAAC,CAAC;QAC9F,EAAE,CAAC,OAAO,CACR;;;;;;oBAMc,CACf,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,cAAc,UAAU,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;QAEvE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC;IACtD,CAAC;YAAS,CAAC;QACT,KAAK,EAAE,CAAC;IACV,CAAC;AACH,CAAC;AAED,SAAgB,yBAAyB,CAAC,SAAiB,EAAE,YAAoB;IAC/E,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,IAAA,iCAAkB,GAAE,CAAC;IAC3C,IAAI,CAAC;QACH,EAAE,CAAC,OAAO,CACR;;;;;;oBAMc,CACf,CAAC,GAAG,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IACjC,CAAC;YAAS,CAAC;QACT,KAAK,EAAE,CAAC;IACV,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-conflict-invoke.d.ts","sourceRoot":"","sources":["../../src/parallel/merge-conflict-invoke.ts"],"names":[],"mappings":"AAKA,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,OAAO,GAAG,UAAU,EAC1B,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,CAiDjB"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.invokeMergeConflictModel = invokeMergeConflictModel;
|
|
4
|
+
const loader_js_1 = require("../config/loader.js");
|
|
5
|
+
const invocation_logger_js_1 = require("../providers/invocation-logger.js");
|
|
6
|
+
const registry_js_1 = require("../providers/registry.js");
|
|
7
|
+
const merge_errors_js_1 = require("./merge-errors.js");
|
|
8
|
+
async function invokeMergeConflictModel(role, projectPath, taskId, prompt) {
|
|
9
|
+
const config = (0, loader_js_1.loadConfig)(projectPath);
|
|
10
|
+
const modelConfig = role === 'coder' ? config.ai?.coder : config.ai?.reviewer;
|
|
11
|
+
if (!modelConfig?.provider || !modelConfig?.model) {
|
|
12
|
+
throw new merge_errors_js_1.ParallelMergeError(`Missing AI ${role} configuration. Configure via config.ai.${role}.`, 'AI_CONFIG_MISSING');
|
|
13
|
+
}
|
|
14
|
+
const providerName = modelConfig.provider;
|
|
15
|
+
const model = modelConfig.model;
|
|
16
|
+
const registry = (0, registry_js_1.getProviderRegistry)();
|
|
17
|
+
const provider = registry.get(providerName);
|
|
18
|
+
const result = await (0, invocation_logger_js_1.logInvocation)(prompt, (ctx) => provider.invoke(prompt, {
|
|
19
|
+
model,
|
|
20
|
+
timeout: 60 * 60 * 1000,
|
|
21
|
+
cwd: projectPath,
|
|
22
|
+
role,
|
|
23
|
+
streamOutput: false,
|
|
24
|
+
onActivity: ctx?.onActivity,
|
|
25
|
+
}), {
|
|
26
|
+
role,
|
|
27
|
+
provider: providerName,
|
|
28
|
+
model,
|
|
29
|
+
taskId,
|
|
30
|
+
projectPath,
|
|
31
|
+
});
|
|
32
|
+
if (!result.success) {
|
|
33
|
+
const details = result.stderr || result.stdout || 'model returned non-zero exit code';
|
|
34
|
+
throw new merge_errors_js_1.ParallelMergeError(`${role.toUpperCase()} invocation failed during merge conflict handling: ${details}`, 'AI_INVOCATION_FAILED');
|
|
35
|
+
}
|
|
36
|
+
if (result.timedOut) {
|
|
37
|
+
throw new merge_errors_js_1.ParallelMergeError(`${role} invocation timed out`, 'AI_INVOKE_TIMEOUT');
|
|
38
|
+
}
|
|
39
|
+
return result.stdout;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=merge-conflict-invoke.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-conflict-invoke.js","sourceRoot":"","sources":["../../src/parallel/merge-conflict-invoke.ts"],"names":[],"mappings":";;AAKA,4DAsDC;AA3DD,mDAAiD;AACjD,4EAAkE;AAClE,0DAA+D;AAC/D,uDAAuD;AAEhD,KAAK,UAAU,wBAAwB,CAC5C,IAA0B,EAC1B,WAAmB,EACnB,MAA0B,EAC1B,MAAc;IAEd,MAAM,MAAM,GAAG,IAAA,sBAAU,EAAC,WAAW,CAAC,CAAC;IACvC,MAAM,WAAW,GAAG,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,QAAQ,CAAC;IAE9E,IAAI,CAAC,WAAW,EAAE,QAAQ,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE,CAAC;QAClD,MAAM,IAAI,oCAAkB,CAC1B,cAAc,IAAI,2CAA2C,IAAI,GAAG,EACpE,mBAAmB,CACpB,CAAC;IACJ,CAAC;IAED,MAAM,YAAY,GAAG,WAAW,CAAC,QAAQ,CAAC;IAC1C,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC;IAEhC,MAAM,QAAQ,GAAG,IAAA,iCAAmB,GAAE,CAAC;IACvC,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,MAAM,IAAA,oCAAa,EAChC,MAAM,EACN,CAAC,GAAG,EAAE,EAAE,CACN,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE;QACtB,KAAK;QACL,OAAO,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;QACvB,GAAG,EAAE,WAAW;QAChB,IAAI;QACJ,YAAY,EAAE,KAAK;QACnB,UAAU,EAAE,GAAG,EAAE,UAAU;KAC5B,CAAC,EACJ;QACE,IAAI;QACJ,QAAQ,EAAE,YAAY;QACtB,KAAK;QACL,MAAM;QACN,WAAW;KACZ,CACF,CAAC;IAEF,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,IAAI,mCAAmC,CAAC;QACtF,MAAM,IAAI,oCAAkB,CAC1B,GAAG,IAAI,CAAC,WAAW,EAAE,sDAAsD,OAAO,EAAE,EACpF,sBAAsB,CACvB,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,MAAM,IAAI,oCAAkB,CAAC,GAAG,IAAI,uBAAuB,EAAE,mBAAmB,CAAC,CAAC;IACpF,CAAC;IAED,OAAO,MAAM,CAAC,MAAM,CAAC;AACvB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface ParseReviewDecisionResult {
|
|
2
|
+
decision: 'approve' | 'reject';
|
|
3
|
+
notes: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function parseReviewDecision(raw: string): ParseReviewDecisionResult;
|
|
6
|
+
export declare function createPromptForConflictCoder(options: {
|
|
7
|
+
workstreamId: string;
|
|
8
|
+
shortSha: string;
|
|
9
|
+
branchName: string;
|
|
10
|
+
commitMessage: string;
|
|
11
|
+
conflictedFiles: string[];
|
|
12
|
+
conflictPatch: string;
|
|
13
|
+
rejectionNotes?: string;
|
|
14
|
+
}): string;
|
|
15
|
+
export declare function createPromptForConflictReviewer(options: {
|
|
16
|
+
workstreamId: string;
|
|
17
|
+
shortSha: string;
|
|
18
|
+
branchName: string;
|
|
19
|
+
commitMessage: string;
|
|
20
|
+
stagedDiff: string;
|
|
21
|
+
stagedFiles: string[];
|
|
22
|
+
}): string;
|
|
23
|
+
//# sourceMappingURL=merge-conflict-prompts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-conflict-prompts.d.ts","sourceRoot":"","sources":["../../src/parallel/merge-conflict-prompts.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,yBAAyB,CAwB1E;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE;IACpD,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GAAG,MAAM,CAMT;AAED,wBAAgB,+BAA+B,CAAC,OAAO,EAAE;IACvD,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,GAAG,MAAM,CAMT"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseReviewDecision = parseReviewDecision;
|
|
4
|
+
exports.createPromptForConflictCoder = createPromptForConflictCoder;
|
|
5
|
+
exports.createPromptForConflictReviewer = createPromptForConflictReviewer;
|
|
6
|
+
function parseReviewDecision(raw) {
|
|
7
|
+
const trimmed = raw.trim();
|
|
8
|
+
const upper = trimmed.toUpperCase();
|
|
9
|
+
const hasApprove = upper.includes('APPROVE');
|
|
10
|
+
const hasReject = upper.includes('REJECT');
|
|
11
|
+
if (hasApprove && !hasReject) {
|
|
12
|
+
return {
|
|
13
|
+
decision: 'approve',
|
|
14
|
+
notes: trimmed || 'APPROVED by merge-conflict reviewer',
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
if (hasReject) {
|
|
18
|
+
return {
|
|
19
|
+
decision: 'reject',
|
|
20
|
+
notes: trimmed || 'Please review and correct conflict resolution',
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
decision: 'reject',
|
|
25
|
+
notes: trimmed || 'Decision was not clear. Please provide explicit APPROVE/REJECT.',
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function createPromptForConflictCoder(options) {
|
|
29
|
+
const notesSection = options.rejectionNotes
|
|
30
|
+
? `\n\nLatest review note from the resolver:\n${options.rejectionNotes}\n`
|
|
31
|
+
: '';
|
|
32
|
+
return `You are resolving a merge conflict for a cherry-pick during parallel merge.\n\n## Conflict context\nWorkstream: ${options.workstreamId}\nBranch: ${options.branchName}\nCommit: ${options.shortSha}\nCommit Message:\n${options.commitMessage}\n\nConflicted files:\n${options.conflictedFiles.map((file) => `- ${file}`).join('\n')}\n\nIntended patch:\n${options.conflictPatch}\n\nRules:\n1) Edit conflicted files to a correct resolution.\n2) Remove ALL conflict markers (<<<<<<, =======, >>>>>>) in resolved files.\n3) Stage only the resolved files using git add.\n4) Do NOT commit.\n5) Be surgical; change only files required for this commit.\n${notesSection}\n\nRespond with a short confirmation when done.`;
|
|
33
|
+
}
|
|
34
|
+
function createPromptForConflictReviewer(options) {
|
|
35
|
+
const files = options.stagedFiles.length > 0
|
|
36
|
+
? options.stagedFiles.map((file) => `- ${file}`).join('\n')
|
|
37
|
+
: 'No files staged yet';
|
|
38
|
+
return `You are reviewing a staged resolution for a cherry-pick conflict in parallel merge.\n\nWorkstream: ${options.workstreamId}\nBranch: ${options.branchName}\nCommit: ${options.shortSha}\nOriginal message: ${options.commitMessage}\n\nCurrent staged diff to be committed by cherry-pick --continue:\n${options.stagedDiff || '(empty diff)'}\n\nFiles staged:\n${files}\n\nDecision rules:\n- Reply with APPROVE if the resolution is correct.\n- Reply with REJECT and actionable notes if any conflict marker remains or logic is incorrect.\n\nFormat:\nAPPROVE - <optional note> or\nREJECT - <checklist itemized note>`;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=merge-conflict-prompts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-conflict-prompts.js","sourceRoot":"","sources":["../../src/parallel/merge-conflict-prompts.ts"],"names":[],"mappings":";;AAKA,kDAwBC;AAED,oEAcC;AAED,0EAaC;AAvDD,SAAgB,mBAAmB,CAAC,GAAW;IAC7C,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IACpC,MAAM,UAAU,GAAG,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAE3C,IAAI,UAAU,IAAI,CAAC,SAAS,EAAE,CAAC;QAC7B,OAAO;YACL,QAAQ,EAAE,SAAS;YACnB,KAAK,EAAE,OAAO,IAAI,qCAAqC;SACxD,CAAC;IACJ,CAAC;IAED,IAAI,SAAS,EAAE,CAAC;QACd,OAAO;YACL,QAAQ,EAAE,QAAQ;YAClB,KAAK,EAAE,OAAO,IAAI,+CAA+C;SAClE,CAAC;IACJ,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,QAAQ;QAClB,KAAK,EAAE,OAAO,IAAI,iEAAiE;KACpF,CAAC;AACJ,CAAC;AAED,SAAgB,4BAA4B,CAAC,OAQ5C;IACC,MAAM,YAAY,GAAG,OAAO,CAAC,cAAc;QACzC,CAAC,CAAC,8CAA8C,OAAO,CAAC,cAAc,IAAI;QAC1E,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO,mHAAmH,OAAO,CAAC,YAAY,aAAa,OAAO,CAAC,UAAU,aAAa,OAAO,CAAC,QAAQ,sBAAsB,OAAO,CAAC,aAAa,0BAA0B,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,wBAAwB,OAAO,CAAC,aAAa,gRAAgR,YAAY,kDAAkD,CAAC;AAC1sB,CAAC;AAED,SAAgB,+BAA+B,CAAC,OAO/C;IACC,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC;QAC1C,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QAC3D,CAAC,CAAC,qBAAqB,CAAC;IAE1B,OAAO,sGAAsG,OAAO,CAAC,YAAY,aAAa,OAAO,CAAC,UAAU,aAAa,OAAO,CAAC,QAAQ,uBAAuB,OAAO,CAAC,aAAa,uEAAuE,OAAO,CAAC,UAAU,IAAI,cAAc,sBAAsB,KAAK,sPAAsP,CAAC;AACxmB,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
|
+
export declare function ensureMergeConflictTask(db: Database.Database, workstreamId: string, shortSha: string, branchName: string, commitMessage: string, conflictedFiles: string[], conflictPatch: string, forceNew?: boolean): string;
|
|
3
|
+
//# sourceMappingURL=merge-conflict-task.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-conflict-task.d.ts","sourceRoot":"","sources":["../../src/parallel/merge-conflict-task.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAqC3C,wBAAgB,uBAAuB,CACrC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,EACrB,eAAe,EAAE,MAAM,EAAE,EACzB,aAAa,EAAE,MAAM,EACrB,QAAQ,UAAQ,GACf,MAAM,CA2CR"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ensureMergeConflictTask = ensureMergeConflictTask;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
const queries_js_1 = require("../database/queries.js");
|
|
6
|
+
function buildMergeConflictSectionName() {
|
|
7
|
+
return 'merge-conflicts';
|
|
8
|
+
}
|
|
9
|
+
function getNowISOString() {
|
|
10
|
+
return new Date().toISOString();
|
|
11
|
+
}
|
|
12
|
+
function createMergeConflictSection(db) {
|
|
13
|
+
const sectionName = buildMergeConflictSectionName();
|
|
14
|
+
const existing = db
|
|
15
|
+
.prepare('SELECT id FROM sections WHERE name = ? LIMIT 1')
|
|
16
|
+
.get(sectionName);
|
|
17
|
+
if (existing) {
|
|
18
|
+
return existing.id;
|
|
19
|
+
}
|
|
20
|
+
const maxPosRow = db
|
|
21
|
+
.prepare('SELECT MAX(position) as maxPos FROM sections')
|
|
22
|
+
.get();
|
|
23
|
+
const position = (maxPosRow?.maxPos ?? -1) + 1;
|
|
24
|
+
const sectionId = (0, node_crypto_1.createHash)('sha1').update(sectionName + position).digest('hex');
|
|
25
|
+
db.prepare(`INSERT INTO sections (id, name, position, priority, skipped, created_at)
|
|
26
|
+
VALUES (?, ?, ?, ?, ?, ?)`).run(sectionId, sectionName, position, 80, 0, getNowISOString());
|
|
27
|
+
return sectionId;
|
|
28
|
+
}
|
|
29
|
+
function ensureMergeConflictTask(db, workstreamId, shortSha, branchName, commitMessage, conflictedFiles, conflictPatch, forceNew = false) {
|
|
30
|
+
const sectionId = createMergeConflictSection(db);
|
|
31
|
+
if (!forceNew) {
|
|
32
|
+
const existing = db
|
|
33
|
+
.prepare(`SELECT t.id
|
|
34
|
+
FROM tasks t
|
|
35
|
+
INNER JOIN sections s ON s.id = t.section_id
|
|
36
|
+
WHERE s.name = ? AND t.title LIKE ?
|
|
37
|
+
ORDER BY t.created_at DESC
|
|
38
|
+
LIMIT 1`)
|
|
39
|
+
.get(buildMergeConflictSectionName(), `Merge conflict: cherry-pick ${shortSha}%`);
|
|
40
|
+
if (existing?.id) {
|
|
41
|
+
return existing.id;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const title = `Merge conflict: cherry-pick ${shortSha} from ${branchName}`;
|
|
45
|
+
const created = (0, queries_js_1.createTask)(db, title, {
|
|
46
|
+
sectionId,
|
|
47
|
+
sourceFile: `merge-conflict (${workstreamId})`,
|
|
48
|
+
filePath: conflictedFiles.join(', '),
|
|
49
|
+
status: 'pending',
|
|
50
|
+
fileContentHash: conflictPatch.substring(0, 2048),
|
|
51
|
+
fileCommitSha: commitMessage,
|
|
52
|
+
});
|
|
53
|
+
(0, queries_js_1.addAuditEntry)(db, created.id, 'null', 'pending', 'merge', {
|
|
54
|
+
actorType: 'orchestrator',
|
|
55
|
+
notes: `Generated from conflict while cherry-picking ${shortSha} from ${branchName}:\n${commitMessage}`,
|
|
56
|
+
});
|
|
57
|
+
return created.id;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=merge-conflict-task.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-conflict-task.js","sourceRoot":"","sources":["../../src/parallel/merge-conflict-task.ts"],"names":[],"mappings":";;AAqCA,0DAoDC;AAxFD,6CAAyC;AACzC,uDAAmE;AAEnE,SAAS,6BAA6B;IACpC,OAAO,iBAAiB,CAAC;AAC3B,CAAC;AAED,SAAS,eAAe;IACtB,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAClC,CAAC;AAED,SAAS,0BAA0B,CAAC,EAAqB;IACvD,MAAM,WAAW,GAAG,6BAA6B,EAAE,CAAC;IACpD,MAAM,QAAQ,GAAG,EAAE;SAChB,OAAO,CAAC,gDAAgD,CAAC;SACzD,GAAG,CAAC,WAAW,CAA+B,CAAC;IAElD,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,QAAQ,CAAC,EAAE,CAAC;IACrB,CAAC;IAED,MAAM,SAAS,GAAG,EAAE;SACjB,OAAO,CAAC,8CAA8C,CAAC;SACvD,GAAG,EAA+B,CAAC;IAEtC,MAAM,QAAQ,GAAG,CAAC,SAAS,EAAE,MAAM,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,IAAA,wBAAU,EAAC,MAAM,CAAC,CAAC,MAAM,CAAC,WAAW,GAAG,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAElF,EAAE,CAAC,OAAO,CACR;+BAC2B,CAC5B,CAAC,GAAG,CAAC,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,EAAE,eAAe,EAAE,CAAC,CAAC;IAElE,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAgB,uBAAuB,CACrC,EAAqB,EACrB,YAAoB,EACpB,QAAgB,EAChB,UAAkB,EAClB,aAAqB,EACrB,eAAyB,EACzB,aAAqB,EACrB,QAAQ,GAAG,KAAK;IAEhB,MAAM,SAAS,GAAG,0BAA0B,CAAC,EAAE,CAAC,CAAC;IAEjD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,QAAQ,GAAG,EAAE;aAChB,OAAO,CACN;;;;;iBAKS,CACV;aACA,GAAG,CAAC,6BAA6B,EAAE,EAAE,+BAA+B,QAAQ,GAAG,CAA+B,CAAC;QAElH,IAAI,QAAQ,EAAE,EAAE,EAAE,CAAC;YACjB,OAAO,QAAQ,CAAC,EAAE,CAAC;QACrB,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAG,+BAA+B,QAAQ,SAAS,UAAU,EAAE,CAAC;IAC3E,MAAM,OAAO,GAAG,IAAA,uBAAU,EAAC,EAAE,EAAE,KAAK,EAAE;QACpC,SAAS;QACT,UAAU,EAAE,mBAAmB,YAAY,GAAG;QAC9C,QAAQ,EAAE,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC;QACpC,MAAM,EAAE,SAAS;QACjB,eAAe,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC;QACjD,aAAa,EAAE,aAAa;KAC7B,CAAC,CAAC;IAEH,IAAA,0BAAa,EACX,EAAE,EACF,OAAO,CAAC,EAAE,EACV,MAAM,EACN,SAAS,EACT,OAAO,EACP;QACE,SAAS,EAAE,cAAc;QACzB,KAAK,EAAE,gDAAgD,QAAQ,SAAS,UAAU,MAAM,aAAa,EAAE;KACxG,CACF,CAAC;IAEF,OAAO,OAAO,CAAC,EAAE,CAAC;AACpB,CAAC"}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Merge conflict resolution
|
|
2
|
+
* Merge conflict resolution orchestrator.
|
|
3
3
|
*/
|
|
4
4
|
import type Database from 'better-sqlite3';
|
|
5
|
-
|
|
6
|
-
decision: 'approve' | 'reject';
|
|
7
|
-
notes: string;
|
|
8
|
-
}
|
|
5
|
+
export { parseReviewDecision } from './merge-conflict-prompts.js';
|
|
9
6
|
export interface ConflictRunOptions {
|
|
10
7
|
db: Database.Database;
|
|
11
8
|
projectPath: string;
|
|
12
9
|
sessionId: string;
|
|
13
10
|
workstreamId: string;
|
|
11
|
+
runnerId: string;
|
|
12
|
+
mergeLockHeartbeat?: {
|
|
13
|
+
lockEpoch: number;
|
|
14
|
+
timeoutMinutes: number;
|
|
15
|
+
};
|
|
14
16
|
branchName: string;
|
|
15
17
|
position: number;
|
|
16
18
|
commitSha: string;
|
|
17
19
|
existingTaskId?: string;
|
|
18
20
|
}
|
|
19
|
-
export declare function parseReviewDecision(raw: string): ParseReviewDecisionResult;
|
|
20
21
|
export declare function runConflictResolutionCycle(options: ConflictRunOptions): Promise<'continued' | 'skipped'>;
|
|
21
|
-
export {};
|
|
22
22
|
//# sourceMappingURL=merge-conflict.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge-conflict.d.ts","sourceRoot":"","sources":["../../src/parallel/merge-conflict.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"merge-conflict.d.ts","sourceRoot":"","sources":["../../src/parallel/merge-conflict.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAkC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAElE,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,QAAQ,CAAC,QAAQ,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,CAAC,EAAE;QACnB,SAAS,EAAE,MAAM,CAAC;QAClB,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAwGD,wBAAsB,0BAA0B,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAyN9G"}
|
|
@@ -1,145 +1,85 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Merge conflict resolution
|
|
3
|
+
* Merge conflict resolution orchestrator.
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.parseReviewDecision =
|
|
6
|
+
exports.parseReviewDecision = void 0;
|
|
7
7
|
exports.runConflictResolutionCycle = runConflictResolutionCycle;
|
|
8
|
-
const node_crypto_1 = require("node:crypto");
|
|
9
|
-
const loader_js_1 = require("../config/loader.js");
|
|
10
8
|
const queries_js_1 = require("../database/queries.js");
|
|
11
|
-
const invocation_logger_js_1 = require("../providers/invocation-logger.js");
|
|
12
|
-
const registry_js_1 = require("../providers/registry.js");
|
|
13
9
|
const merge_git_js_1 = require("./merge-git.js");
|
|
14
10
|
const merge_errors_js_1 = require("./merge-errors.js");
|
|
15
11
|
const merge_progress_js_1 = require("./merge-progress.js");
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
decision: 'reject',
|
|
35
|
-
notes: trimmed || 'Decision was not clear. Please provide explicit APPROVE/REJECT.',
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
function buildMergeConflictSectionName() {
|
|
39
|
-
return 'merge-conflicts';
|
|
40
|
-
}
|
|
41
|
-
function getNowISOString() {
|
|
42
|
-
return new Date().toISOString();
|
|
43
|
-
}
|
|
44
|
-
function createMergeConflictSection(db) {
|
|
45
|
-
const sectionName = buildMergeConflictSectionName();
|
|
46
|
-
const existing = db
|
|
47
|
-
.prepare('SELECT id FROM sections WHERE name = ? LIMIT 1')
|
|
48
|
-
.get(sectionName);
|
|
49
|
-
if (existing) {
|
|
50
|
-
return existing.id;
|
|
51
|
-
}
|
|
52
|
-
const maxPosRow = db
|
|
53
|
-
.prepare('SELECT MAX(position) as maxPos FROM sections')
|
|
54
|
-
.get();
|
|
55
|
-
const position = (maxPosRow?.maxPos ?? -1) + 1;
|
|
56
|
-
const sectionId = (0, node_crypto_1.createHash)('sha1').update(sectionName + position).digest('hex');
|
|
57
|
-
db.prepare(`INSERT INTO sections (id, name, position, priority, skipped, created_at)
|
|
58
|
-
VALUES (?, ?, ?, ?, ?, ?)`).run(sectionId, sectionName, position, 80, 0, getNowISOString());
|
|
59
|
-
return sectionId;
|
|
60
|
-
}
|
|
61
|
-
function ensureMergeConflictTask(db, workstreamId, shortSha, branchName, commitMessage, conflictedFiles, conflictPatch, forceNew = false) {
|
|
62
|
-
const sectionId = createMergeConflictSection(db);
|
|
63
|
-
if (!forceNew) {
|
|
64
|
-
const existing = db
|
|
65
|
-
.prepare(`SELECT t.id
|
|
66
|
-
FROM tasks t
|
|
67
|
-
INNER JOIN sections s ON s.id = t.section_id
|
|
68
|
-
WHERE s.name = ? AND t.title LIKE ?
|
|
69
|
-
ORDER BY t.created_at DESC
|
|
12
|
+
const global_db_js_1 = require("../runners/global-db.js");
|
|
13
|
+
const merge_conflict_attempts_js_1 = require("./merge-conflict-attempts.js");
|
|
14
|
+
const merge_conflict_invoke_js_1 = require("./merge-conflict-invoke.js");
|
|
15
|
+
const merge_conflict_prompts_js_1 = require("./merge-conflict-prompts.js");
|
|
16
|
+
const merge_conflict_task_js_1 = require("./merge-conflict-task.js");
|
|
17
|
+
const merge_lock_js_1 = require("./merge-lock.js");
|
|
18
|
+
var merge_conflict_prompts_js_2 = require("./merge-conflict-prompts.js");
|
|
19
|
+
Object.defineProperty(exports, "parseReviewDecision", { enumerable: true, get: function () { return merge_conflict_prompts_js_2.parseReviewDecision; } });
|
|
20
|
+
function refreshMergeConflictLease(sessionId, workstreamId, projectPath, runnerId) {
|
|
21
|
+
const { db, close } = (0, global_db_js_1.openGlobalDatabase)();
|
|
22
|
+
try {
|
|
23
|
+
const row = db
|
|
24
|
+
.prepare(`SELECT id, claim_generation, runner_id
|
|
25
|
+
FROM workstreams
|
|
26
|
+
WHERE session_id = ?
|
|
27
|
+
AND id = ?
|
|
28
|
+
AND clone_path = ?
|
|
29
|
+
AND status IN ('running', 'completed')
|
|
70
30
|
LIMIT 1`)
|
|
71
|
-
.get(
|
|
72
|
-
if (
|
|
73
|
-
|
|
31
|
+
.get(sessionId, workstreamId, projectPath);
|
|
32
|
+
if (!row) {
|
|
33
|
+
throw new merge_errors_js_1.ParallelMergeError('Parallel workstream lease row not found during conflict resolution', 'LEASE_ROW_MISSING');
|
|
34
|
+
}
|
|
35
|
+
if (row.runner_id !== null && row.runner_id !== runnerId) {
|
|
36
|
+
throw new merge_errors_js_1.ParallelMergeError(`Parallel workstream lease owned by ${row.runner_id}, not ${runnerId}`, 'LEASE_FENCE_FAILED');
|
|
37
|
+
}
|
|
38
|
+
const update = db
|
|
39
|
+
.prepare(`UPDATE workstreams
|
|
40
|
+
SET runner_id = ?,
|
|
41
|
+
lease_expires_at = datetime('now', '+120 seconds')
|
|
42
|
+
WHERE id = ?
|
|
43
|
+
AND status IN ('running', 'completed')
|
|
44
|
+
AND claim_generation = ?
|
|
45
|
+
AND (runner_id IS NULL OR runner_id = ?)`)
|
|
46
|
+
.run(runnerId, row.id, row.claim_generation, runnerId);
|
|
47
|
+
if (update.changes !== 1) {
|
|
48
|
+
throw new merge_errors_js_1.ParallelMergeError('Parallel workstream lease fence check failed during conflict resolution', 'LEASE_FENCE_FAILED');
|
|
74
49
|
}
|
|
75
50
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
sectionId,
|
|
79
|
-
sourceFile: `merge-conflict (${workstreamId})`,
|
|
80
|
-
filePath: conflictedFiles.join(', '),
|
|
81
|
-
status: 'pending',
|
|
82
|
-
fileContentHash: conflictPatch.substring(0, 2048),
|
|
83
|
-
fileCommitSha: commitMessage,
|
|
84
|
-
});
|
|
85
|
-
(0, queries_js_1.addAuditEntry)(db, created.id, 'null', 'pending', 'merge', {
|
|
86
|
-
actorType: 'orchestrator',
|
|
87
|
-
notes: `Generated from conflict while cherry-picking ${shortSha} from ${branchName}:\n${commitMessage}`,
|
|
88
|
-
});
|
|
89
|
-
return created.id;
|
|
90
|
-
}
|
|
91
|
-
function createPromptForConflictCoder(options) {
|
|
92
|
-
const notesSection = options.rejectionNotes
|
|
93
|
-
? `\n\nLatest review note from the resolver:\n${options.rejectionNotes}\n`
|
|
94
|
-
: '';
|
|
95
|
-
return `You are resolving a merge conflict for a cherry-pick during parallel merge.\n\n## Conflict context\nWorkstream: ${options.workstreamId}\nBranch: ${options.branchName}\nCommit: ${options.shortSha}\nCommit Message:\n${options.commitMessage}\n\nConflicted files:\n${options.conflictedFiles.map((file) => `- ${file}`).join('\n')}\n\nIntended patch:\n${options.conflictPatch}\n\nRules:\n1) Edit conflicted files to a correct resolution.\n2) Remove ALL conflict markers (<<<<<<, =======, >>>>>>) in resolved files.\n3) Stage only the resolved files using git add.\n4) Do NOT commit.\n5) Be surgical; change only files required for this commit.\n${notesSection}\n\nRespond with a short confirmation when done.`;
|
|
96
|
-
}
|
|
97
|
-
function createPromptForConflictReviewer(options) {
|
|
98
|
-
const files = options.stagedFiles.length > 0
|
|
99
|
-
? options.stagedFiles.map((file) => `- ${file}`).join('\n')
|
|
100
|
-
: 'No files staged yet';
|
|
101
|
-
return `You are reviewing a staged resolution for a cherry-pick conflict in parallel merge.\n\nWorkstream: ${options.workstreamId}\nBranch: ${options.branchName}\nCommit: ${options.shortSha}\nOriginal message: ${options.commitMessage}\n\nCurrent staged diff to be committed by cherry-pick --continue:\n${options.stagedDiff || '(empty diff)'}\n\nFiles staged:\n${files}\n\nDecision rules:\n- Reply with APPROVE if the resolution is correct.\n- Reply with REJECT and actionable notes if any conflict marker remains or logic is incorrect.\n\nFormat:\nAPPROVE - <optional note> or\nREJECT - <checklist itemized note>`;
|
|
102
|
-
}
|
|
103
|
-
async function invokeModel(role, projectPath, taskId, prompt) {
|
|
104
|
-
const config = (0, loader_js_1.loadConfig)(projectPath);
|
|
105
|
-
const modelConfig = role === 'coder' ? config.ai?.coder : config.ai?.reviewer;
|
|
106
|
-
if (!modelConfig?.provider || !modelConfig?.model) {
|
|
107
|
-
throw new merge_errors_js_1.ParallelMergeError(`Missing AI ${role} configuration. Configure via config.ai.${role}.`, 'AI_CONFIG_MISSING');
|
|
51
|
+
finally {
|
|
52
|
+
close();
|
|
108
53
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const result = await (0, invocation_logger_js_1.logInvocation)(prompt, (ctx) => provider.invoke(prompt, {
|
|
114
|
-
model,
|
|
115
|
-
timeout: 60 * 60 * 1000,
|
|
116
|
-
cwd: projectPath,
|
|
117
|
-
role,
|
|
118
|
-
streamOutput: false,
|
|
119
|
-
onActivity: ctx?.onActivity,
|
|
120
|
-
}), {
|
|
121
|
-
role,
|
|
122
|
-
provider: providerName,
|
|
123
|
-
model,
|
|
124
|
-
taskId,
|
|
125
|
-
projectPath,
|
|
54
|
+
}
|
|
55
|
+
function delay(ms) {
|
|
56
|
+
return new Promise((resolve) => {
|
|
57
|
+
setTimeout(resolve, ms);
|
|
126
58
|
});
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
59
|
+
}
|
|
60
|
+
async function waitForConflictRetryWindow(db, sessionId, workstreamId, projectPath, runnerId, backoffMinutes, mergeLockHeartbeat) {
|
|
61
|
+
if (backoffMinutes <= 0) {
|
|
62
|
+
return;
|
|
130
63
|
}
|
|
131
|
-
|
|
132
|
-
|
|
64
|
+
let remainingMs = backoffMinutes * 60_000;
|
|
65
|
+
const heartbeatSliceMs = 30_000;
|
|
66
|
+
while (remainingMs > 0) {
|
|
67
|
+
const waitMs = Math.min(heartbeatSliceMs, remainingMs);
|
|
68
|
+
await delay(waitMs);
|
|
69
|
+
remainingMs -= waitMs;
|
|
70
|
+
refreshMergeConflictLease(sessionId, workstreamId, projectPath, runnerId);
|
|
71
|
+
if (mergeLockHeartbeat) {
|
|
72
|
+
(0, merge_lock_js_1.refreshMergeLock)(db, sessionId, runnerId, mergeLockHeartbeat.timeoutMinutes, mergeLockHeartbeat.lockEpoch);
|
|
73
|
+
}
|
|
133
74
|
}
|
|
134
|
-
return result.stdout;
|
|
135
75
|
}
|
|
136
76
|
async function runConflictResolutionCycle(options) {
|
|
137
|
-
const { db, projectPath, sessionId, workstreamId, branchName, position, commitSha, existingTaskId, } = options;
|
|
138
|
-
const shortSha =
|
|
77
|
+
const { db, projectPath, sessionId, workstreamId, runnerId, mergeLockHeartbeat, branchName, position, commitSha, existingTaskId, } = options;
|
|
78
|
+
const shortSha = commitSha.slice(0, 7);
|
|
139
79
|
const conflictedFiles = (0, merge_git_js_1.getConflictedFiles)(projectPath);
|
|
140
80
|
const conflictPatch = (0, merge_git_js_1.getCommitPatch)(projectPath, commitSha);
|
|
141
81
|
const commitMessage = (0, merge_git_js_1.getCommitMessage)(projectPath, commitSha);
|
|
142
|
-
const taskId = ensureMergeConflictTask(db, workstreamId, shortSha, branchName, commitMessage, conflictedFiles, conflictPatch, !existingTaskId);
|
|
82
|
+
const taskId = (0, merge_conflict_task_js_1.ensureMergeConflictTask)(db, workstreamId, shortSha, branchName, commitMessage, conflictedFiles, conflictPatch, !existingTaskId);
|
|
143
83
|
let conflictTaskId = existingTaskId ?? taskId;
|
|
144
84
|
if (conflictTaskId !== taskId) {
|
|
145
85
|
conflictTaskId = taskId;
|
|
@@ -149,17 +89,32 @@ async function runConflictResolutionCycle(options) {
|
|
|
149
89
|
if (!currentConflictTask) {
|
|
150
90
|
throw new merge_errors_js_1.ParallelMergeError('Created merge-conflict task not found', 'TASK_MISSING');
|
|
151
91
|
}
|
|
92
|
+
refreshMergeConflictLease(sessionId, workstreamId, projectPath, runnerId);
|
|
152
93
|
if (currentConflictTask.status === 'completed') {
|
|
153
|
-
(0,
|
|
94
|
+
const appliedCommitSha = (0, merge_git_js_1.runGitCommand)(projectPath, ['rev-parse', 'HEAD'], { allowFailure: true }).trim();
|
|
95
|
+
(0, merge_progress_js_1.upsertProgressEntry)(db, sessionId, workstreamId, position, commitSha, 'applied', conflictTaskId, appliedCommitSha || null);
|
|
96
|
+
(0, merge_conflict_attempts_js_1.clearConflictAttemptState)(sessionId, workstreamId);
|
|
154
97
|
return 'continued';
|
|
155
98
|
}
|
|
156
99
|
(0, queries_js_1.updateTaskStatus)(db, currentConflictTask.id, 'in_progress', 'merge-conflict-orchestrator');
|
|
157
100
|
while (true) {
|
|
101
|
+
const conflictAttempt = (0, merge_conflict_attempts_js_1.recordConflictAttempt)(sessionId, workstreamId);
|
|
102
|
+
if (conflictAttempt.blocked) {
|
|
103
|
+
(0, queries_js_1.rejectTask)(db, currentConflictTask.id, 'merge-conflict-reviewer', `Conflict resolution attempt limit reached (${merge_conflict_attempts_js_1.MAX_CONFLICT_ATTEMPTS}). Session moved to blocked_conflict.`);
|
|
104
|
+
throw new merge_errors_js_1.ParallelMergeError(`Conflict resolution exceeded ${merge_conflict_attempts_js_1.MAX_CONFLICT_ATTEMPTS} attempts for ${shortSha}`, 'CONFLICT_ATTEMPT_LIMIT');
|
|
105
|
+
}
|
|
106
|
+
refreshMergeConflictLease(sessionId, workstreamId, projectPath, runnerId);
|
|
107
|
+
if (conflictAttempt.attempts > 1 && (conflictAttempt.backoffMinutes ?? 0) > 0) {
|
|
108
|
+
(0, queries_js_1.updateTaskStatus)(db, currentConflictTask.id, 'in_progress', 'merge-conflict-orchestrator', `Waiting ${conflictAttempt.backoffMinutes} minute(s) before retry ${conflictAttempt.attempts}.`);
|
|
109
|
+
await waitForConflictRetryWindow(db, sessionId, workstreamId, projectPath, runnerId, conflictAttempt.backoffMinutes ?? 0, mergeLockHeartbeat);
|
|
110
|
+
}
|
|
158
111
|
const existingTask = (0, queries_js_1.getTask)(db, currentConflictTask.id);
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
112
|
+
const noteParts = [`Attempt ${conflictAttempt.attempts}/${merge_conflict_attempts_js_1.MAX_CONFLICT_ATTEMPTS}.`];
|
|
113
|
+
if (existingTask?.rejection_count) {
|
|
114
|
+
noteParts.push(`After ${existingTask.rejection_count} rejection(s).`);
|
|
115
|
+
}
|
|
116
|
+
const lastNotes = noteParts.join(' ');
|
|
117
|
+
const coderPrompt = (0, merge_conflict_prompts_js_1.createPromptForConflictCoder)({
|
|
163
118
|
workstreamId,
|
|
164
119
|
shortSha,
|
|
165
120
|
branchName,
|
|
@@ -168,7 +123,7 @@ async function runConflictResolutionCycle(options) {
|
|
|
168
123
|
conflictPatch,
|
|
169
124
|
rejectionNotes: lastNotes,
|
|
170
125
|
});
|
|
171
|
-
await
|
|
126
|
+
await (0, merge_conflict_invoke_js_1.invokeMergeConflictModel)('coder', projectPath, currentConflictTask.id, coderPrompt);
|
|
172
127
|
const remaining = (0, merge_git_js_1.getConflictedFiles)(projectPath);
|
|
173
128
|
if (remaining.length > 0) {
|
|
174
129
|
(0, queries_js_1.updateTaskStatus)(db, currentConflictTask.id, 'in_progress', 'merge-conflict-orchestrator', `Conflict markers still present: ${remaining.join(', ')}`);
|
|
@@ -181,7 +136,7 @@ async function runConflictResolutionCycle(options) {
|
|
|
181
136
|
continue;
|
|
182
137
|
}
|
|
183
138
|
(0, queries_js_1.updateTaskStatus)(db, currentConflictTask.id, 'review', 'merge-conflict-orchestrator');
|
|
184
|
-
const reviewerPrompt = createPromptForConflictReviewer({
|
|
139
|
+
const reviewerPrompt = (0, merge_conflict_prompts_js_1.createPromptForConflictReviewer)({
|
|
185
140
|
workstreamId,
|
|
186
141
|
shortSha,
|
|
187
142
|
branchName,
|
|
@@ -189,14 +144,10 @@ async function runConflictResolutionCycle(options) {
|
|
|
189
144
|
stagedDiff,
|
|
190
145
|
stagedFiles,
|
|
191
146
|
});
|
|
192
|
-
const decisionText = await
|
|
193
|
-
const decision = parseReviewDecision(decisionText);
|
|
147
|
+
const decisionText = await (0, merge_conflict_invoke_js_1.invokeMergeConflictModel)('reviewer', projectPath, currentConflictTask.id, reviewerPrompt);
|
|
148
|
+
const decision = (0, merge_conflict_prompts_js_1.parseReviewDecision)(decisionText);
|
|
194
149
|
if (decision.decision === 'reject') {
|
|
195
150
|
(0, queries_js_1.rejectTask)(db, currentConflictTask.id, 'merge-conflict-reviewer', decision.notes);
|
|
196
|
-
if ((0, merge_git_js_1.cleanTreeHasConflicts)(projectPath)) {
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
|
-
// If no explicit conflict markers remain but reviewer still rejects, keep iterating.
|
|
200
151
|
continue;
|
|
201
152
|
}
|
|
202
153
|
if ((0, merge_git_js_1.hasUnmergedFiles)(projectPath)) {
|
|
@@ -207,17 +158,22 @@ async function runConflictResolutionCycle(options) {
|
|
|
207
158
|
throw new merge_errors_js_1.ParallelMergeError('Cherry-pick no longer in progress while resolving conflict', 'CHERRY_PICK_CONTEXT_LOST');
|
|
208
159
|
}
|
|
209
160
|
try {
|
|
161
|
+
refreshMergeConflictLease(sessionId, workstreamId, projectPath, runnerId);
|
|
210
162
|
(0, merge_git_js_1.runGitCommand)(projectPath, ['-c', 'core.editor=true', 'cherry-pick', '--continue']);
|
|
211
163
|
(0, queries_js_1.approveTask)(db, currentConflictTask.id, 'merge-conflict-reviewer', decision.notes);
|
|
212
|
-
(0,
|
|
164
|
+
const appliedCommitSha = (0, merge_git_js_1.runGitCommand)(projectPath, ['rev-parse', 'HEAD'], { allowFailure: true }).trim();
|
|
165
|
+
(0, merge_progress_js_1.upsertProgressEntry)(db, sessionId, workstreamId, position, commitSha, 'applied', currentConflictTask.id, appliedCommitSha || null);
|
|
166
|
+
(0, merge_conflict_attempts_js_1.clearConflictAttemptState)(sessionId, workstreamId);
|
|
213
167
|
return 'continued';
|
|
214
168
|
}
|
|
215
169
|
catch (error) {
|
|
216
170
|
const message = error instanceof Error ? error.message : String(error);
|
|
217
171
|
if (/nothing to commit|previous cherry-pick is empty/i.test(message)) {
|
|
172
|
+
refreshMergeConflictLease(sessionId, workstreamId, projectPath, runnerId);
|
|
218
173
|
(0, merge_git_js_1.runGitCommand)(projectPath, ['cherry-pick', '--skip']);
|
|
219
174
|
(0, merge_progress_js_1.upsertProgressEntry)(db, sessionId, workstreamId, position, commitSha, 'skipped', currentConflictTask.id);
|
|
220
175
|
(0, queries_js_1.updateTaskStatus)(db, currentConflictTask.id, 'completed', 'merge-conflict-reviewer', 'Cherry-pick is now empty after resolution; skipped this commit.');
|
|
176
|
+
(0, merge_conflict_attempts_js_1.clearConflictAttemptState)(sessionId, workstreamId);
|
|
221
177
|
return 'skipped';
|
|
222
178
|
}
|
|
223
179
|
throw error;
|