dialectic 0.5.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/debate.d.ts.map +1 -1
- package/dist/commands/debate.js +218 -113
- package/dist/commands/debate.js.map +1 -1
- package/dist/commands/debate.spec.js +1801 -106
- package/dist/commands/debate.spec.js.map +1 -1
- package/dist/commands/eval-requirements.d.ts.map +1 -1
- package/dist/commands/eval-requirements.js +2 -1
- package/dist/commands/eval-requirements.js.map +1 -1
- package/dist/commands/eval-requirements.spec.js +521 -0
- package/dist/commands/eval-requirements.spec.js.map +1 -1
- package/dist/commands/eval.d.ts.map +1 -1
- package/dist/commands/eval.js +27 -25
- package/dist/commands/eval.js.map +1 -1
- package/dist/commands/eval.spec.js +612 -6
- package/dist/commands/eval.spec.js.map +1 -1
- package/dist/commands/report.d.ts.map +1 -1
- package/dist/commands/report.js +12 -10
- package/dist/commands/report.js.map +1 -1
- package/dist/commands/report.spec.js +244 -7
- package/dist/commands/report.spec.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/utils/progress-ui.js +6 -6
- package/dist/utils/progress-ui.js.map +1 -1
- package/dist/utils/progress-ui.spec.js +145 -1
- package/dist/utils/progress-ui.spec.js.map +1 -1
- package/package.json +54 -54
|
@@ -1,13 +1,48 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
const fs_1 = __importDefault(require("fs"));
|
|
6
40
|
const os_1 = __importDefault(require("os"));
|
|
7
41
|
const path_1 = __importDefault(require("path"));
|
|
8
|
-
const
|
|
9
|
-
const index_1 = require("../index");
|
|
42
|
+
const dialecticCore = __importStar(require("dialectic-core"));
|
|
10
43
|
const dialectic_core_1 = require("dialectic-core");
|
|
44
|
+
const indexModule = __importStar(require("../index"));
|
|
45
|
+
const index_1 = require("../index");
|
|
11
46
|
const debate_1 = require("./debate");
|
|
12
47
|
const MOCK_SOLUTION_TEXT = 'Solution text';
|
|
13
48
|
jest.mock('openai', () => {
|
|
@@ -16,10 +51,11 @@ jest.mock('openai', () => {
|
|
|
16
51
|
default: class OpenAIMock {
|
|
17
52
|
chat = {
|
|
18
53
|
completions: {
|
|
19
|
-
create: async (
|
|
54
|
+
create: async () => ({ choices: [{ message: { content: MOCK_SOLUTION_TEXT } }] }),
|
|
20
55
|
},
|
|
21
56
|
};
|
|
22
|
-
constructor(
|
|
57
|
+
constructor() {
|
|
58
|
+
}
|
|
23
59
|
},
|
|
24
60
|
};
|
|
25
61
|
});
|
|
@@ -31,25 +67,29 @@ jest.mock('dialectic-core', () => {
|
|
|
31
67
|
return undefined;
|
|
32
68
|
}),
|
|
33
69
|
collectClarifications: jest.fn().mockResolvedValue([]),
|
|
34
|
-
generateDebateReport: jest.fn().mockImplementation(actual.generateDebateReport)
|
|
70
|
+
generateDebateReport: jest.fn().mockImplementation(actual.generateDebateReport),
|
|
71
|
+
validateLangfuseConfig: jest.fn().mockImplementation(actual.validateLangfuseConfig),
|
|
72
|
+
createTracingContext: jest.fn().mockImplementation(actual.createTracingContext),
|
|
73
|
+
logWarning: jest.fn().mockImplementation(actual.logWarning),
|
|
35
74
|
};
|
|
36
75
|
});
|
|
76
|
+
let mockAnswers = [];
|
|
77
|
+
let currentIndex = 0;
|
|
37
78
|
jest.mock('readline', () => {
|
|
38
|
-
let mockAnswers = [];
|
|
39
|
-
let currentIndex = 0;
|
|
40
79
|
return {
|
|
41
80
|
__esModule: true,
|
|
42
81
|
default: {
|
|
43
82
|
createInterface: () => ({
|
|
44
|
-
question: (
|
|
83
|
+
question: (_prompt, cb) => {
|
|
45
84
|
const ans = currentIndex < mockAnswers.length ? mockAnswers[currentIndex++] : '';
|
|
46
|
-
|
|
85
|
+
cb(String(ans));
|
|
86
|
+
},
|
|
87
|
+
close: () => {
|
|
47
88
|
},
|
|
48
|
-
close: () => { },
|
|
49
89
|
})
|
|
50
90
|
},
|
|
51
91
|
__setMockAnswers: (answers) => {
|
|
52
|
-
mockAnswers = answers;
|
|
92
|
+
mockAnswers = [...answers];
|
|
53
93
|
currentIndex = 0;
|
|
54
94
|
}
|
|
55
95
|
};
|
|
@@ -183,7 +223,8 @@ describe('CLI debate command', () => {
|
|
|
183
223
|
try {
|
|
184
224
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
185
225
|
}
|
|
186
|
-
catch {
|
|
226
|
+
catch {
|
|
227
|
+
}
|
|
187
228
|
});
|
|
188
229
|
it('should write JSON output when output path ends with .json', async () => {
|
|
189
230
|
const outputFile = path_1.default.join(tmpDir, 'result.json');
|
|
@@ -246,15 +287,14 @@ describe('CLI debate command', () => {
|
|
|
246
287
|
try {
|
|
247
288
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
248
289
|
}
|
|
249
|
-
catch {
|
|
290
|
+
catch {
|
|
291
|
+
}
|
|
250
292
|
});
|
|
251
|
-
it('should
|
|
293
|
+
it('should error when both problem string and --problemDescription are provided', async () => {
|
|
252
294
|
const problemFile = path_1.default.join(tmpDir, 'problem.txt');
|
|
253
295
|
fs_1.default.writeFileSync(problemFile, 'Problem from file');
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
expect(stdoutWriteSpy).toHaveBeenCalled();
|
|
257
|
-
stdoutWriteSpy.mockRestore();
|
|
296
|
+
await expect((0, index_1.runCli)(['debate', 'Problem from string', '--problemDescription', problemFile, '--rounds', '1']))
|
|
297
|
+
.rejects.toHaveProperty('code', dialectic_core_1.EXIT_INVALID_ARGS);
|
|
258
298
|
});
|
|
259
299
|
it('should read problem from file when --problemDescription is provided', async () => {
|
|
260
300
|
const problemFile = path_1.default.join(tmpDir, 'problem.txt');
|
|
@@ -361,8 +401,7 @@ describe('Configuration loading', () => {
|
|
|
361
401
|
}
|
|
362
402
|
});
|
|
363
403
|
it('should use built-in defaults when agents array is empty', async () => {
|
|
364
|
-
|
|
365
|
-
tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'config-test-'));
|
|
404
|
+
const tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'config-test-'));
|
|
366
405
|
try {
|
|
367
406
|
const configPath = getTestConfigPath(tmpDir);
|
|
368
407
|
const configContent = {
|
|
@@ -380,12 +419,12 @@ describe('Configuration loading', () => {
|
|
|
380
419
|
try {
|
|
381
420
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
382
421
|
}
|
|
383
|
-
catch {
|
|
422
|
+
catch {
|
|
423
|
+
}
|
|
384
424
|
}
|
|
385
425
|
});
|
|
386
426
|
it('should use default judge when judge is missing', async () => {
|
|
387
|
-
|
|
388
|
-
tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'config-test-'));
|
|
427
|
+
const tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'config-test-'));
|
|
389
428
|
try {
|
|
390
429
|
const configPath = getTestConfigPath(tmpDir);
|
|
391
430
|
const configContent = {
|
|
@@ -404,12 +443,12 @@ describe('Configuration loading', () => {
|
|
|
404
443
|
try {
|
|
405
444
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
406
445
|
}
|
|
407
|
-
catch {
|
|
446
|
+
catch {
|
|
447
|
+
}
|
|
408
448
|
}
|
|
409
449
|
});
|
|
410
450
|
it('should use default debate when debate is missing', async () => {
|
|
411
|
-
|
|
412
|
-
tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'config-test-'));
|
|
451
|
+
const tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'config-test-'));
|
|
413
452
|
try {
|
|
414
453
|
const configPath = getTestConfigPath(tmpDir);
|
|
415
454
|
const configContent = {
|
|
@@ -432,12 +471,12 @@ describe('Configuration loading', () => {
|
|
|
432
471
|
try {
|
|
433
472
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
434
473
|
}
|
|
435
|
-
catch {
|
|
474
|
+
catch {
|
|
475
|
+
}
|
|
436
476
|
}
|
|
437
477
|
});
|
|
438
478
|
it('should load config successfully when all fields are present', async () => {
|
|
439
|
-
|
|
440
|
-
tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'config-test-'));
|
|
479
|
+
const tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'config-test-'));
|
|
441
480
|
try {
|
|
442
481
|
const configPath = getTestConfigPath(tmpDir);
|
|
443
482
|
const configContent = createTestConfigContent();
|
|
@@ -452,7 +491,8 @@ describe('Configuration loading', () => {
|
|
|
452
491
|
try {
|
|
453
492
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
454
493
|
}
|
|
455
|
-
catch {
|
|
494
|
+
catch {
|
|
495
|
+
}
|
|
456
496
|
}
|
|
457
497
|
});
|
|
458
498
|
});
|
|
@@ -479,8 +519,7 @@ describe('Debate config validation', () => {
|
|
|
479
519
|
stdoutWriteSpy.mockRestore();
|
|
480
520
|
});
|
|
481
521
|
it('should use sysConfig.debate.rounds when options.rounds is not provided', async () => {
|
|
482
|
-
|
|
483
|
-
tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'config-test-'));
|
|
522
|
+
const tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'config-test-'));
|
|
484
523
|
try {
|
|
485
524
|
const configPath = getTestConfigPath(tmpDir);
|
|
486
525
|
const configContent = createTestConfigContent(undefined, {
|
|
@@ -496,7 +535,8 @@ describe('Debate config validation', () => {
|
|
|
496
535
|
try {
|
|
497
536
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
498
537
|
}
|
|
499
|
-
catch {
|
|
538
|
+
catch {
|
|
539
|
+
}
|
|
500
540
|
}
|
|
501
541
|
});
|
|
502
542
|
it('should use DEFAULT_ROUNDS when neither options.rounds nor sysConfig.debate.rounds is provided', async () => {
|
|
@@ -530,7 +570,8 @@ describe('Agent filtering', () => {
|
|
|
530
570
|
try {
|
|
531
571
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
532
572
|
}
|
|
533
|
-
catch {
|
|
573
|
+
catch {
|
|
574
|
+
}
|
|
534
575
|
process.env = originalEnv;
|
|
535
576
|
stdoutSpy.mockRestore();
|
|
536
577
|
});
|
|
@@ -674,7 +715,8 @@ describe('Prompt resolution branches', () => {
|
|
|
674
715
|
try {
|
|
675
716
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
676
717
|
}
|
|
677
|
-
catch {
|
|
718
|
+
catch {
|
|
719
|
+
}
|
|
678
720
|
process.env = originalEnv;
|
|
679
721
|
stdoutSpy.mockRestore();
|
|
680
722
|
});
|
|
@@ -821,7 +863,8 @@ describe('Tracing context', () => {
|
|
|
821
863
|
try {
|
|
822
864
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
823
865
|
}
|
|
824
|
-
catch {
|
|
866
|
+
catch {
|
|
867
|
+
}
|
|
825
868
|
process.env = originalEnv;
|
|
826
869
|
stdoutSpy.mockRestore();
|
|
827
870
|
consoleErrorSpy.mockRestore();
|
|
@@ -886,14 +929,14 @@ describe('Tracing context', () => {
|
|
|
886
929
|
it('should include contextFileName in metadata when provided', async () => {
|
|
887
930
|
process.env.LANGFUSE_SECRET_KEY = 'test-secret-key';
|
|
888
931
|
process.env.LANGFUSE_PUBLIC_KEY = 'test-public-key';
|
|
889
|
-
const
|
|
890
|
-
fs_1.default.
|
|
932
|
+
const contextDir = path_1.default.join(tmpDir, 'context');
|
|
933
|
+
fs_1.default.mkdirSync(contextDir, { recursive: true });
|
|
891
934
|
const configPath = getTestConfigPath(tmpDir);
|
|
892
935
|
const configContent = createTestConfigContent(undefined, {
|
|
893
936
|
trace: 'langfuse',
|
|
894
937
|
});
|
|
895
938
|
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
896
|
-
await (0, index_1.runCli)(['debate', 'Design a system', '--context',
|
|
939
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--context', contextDir, '--config', configPath, '--rounds', '1']);
|
|
897
940
|
expect(stdoutSpy).toHaveBeenCalled();
|
|
898
941
|
});
|
|
899
942
|
it('should include judgeConfig in metadata when judge exists', async () => {
|
|
@@ -963,7 +1006,8 @@ describe('Verbose header branches', () => {
|
|
|
963
1006
|
try {
|
|
964
1007
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
965
1008
|
}
|
|
966
|
-
catch {
|
|
1009
|
+
catch {
|
|
1010
|
+
}
|
|
967
1011
|
process.env = originalEnv;
|
|
968
1012
|
});
|
|
969
1013
|
it('should show file prompt source in verbose header when prompt is from file', async () => {
|
|
@@ -1071,37 +1115,38 @@ describe('Error handling', () => {
|
|
|
1071
1115
|
}
|
|
1072
1116
|
});
|
|
1073
1117
|
it('should use error code when error has code property', async () => {
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1118
|
+
const testError = Object.assign(new Error('Test error'), { code: dialectic_core_1.EXIT_INVALID_ARGS });
|
|
1119
|
+
const errorWithCode = testError;
|
|
1120
|
+
const code = (errorWithCode && typeof errorWithCode.code === 'number')
|
|
1121
|
+
? errorWithCode.code
|
|
1122
|
+
: dialectic_core_1.EXIT_GENERAL_ERROR;
|
|
1123
|
+
expect(code).toBe(dialectic_core_1.EXIT_INVALID_ARGS);
|
|
1124
|
+
expect(testError.message).toBe('Test error');
|
|
1079
1125
|
});
|
|
1080
1126
|
it('should use EXIT_GENERAL_ERROR when error has no code property', async () => {
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1127
|
+
const testError = new Error('Test error without code');
|
|
1128
|
+
const errorWithCode = testError;
|
|
1129
|
+
const code = (errorWithCode && typeof errorWithCode.code === 'number')
|
|
1130
|
+
? errorWithCode.code
|
|
1131
|
+
: dialectic_core_1.EXIT_GENERAL_ERROR;
|
|
1132
|
+
expect(code).toBe(dialectic_core_1.EXIT_GENERAL_ERROR);
|
|
1133
|
+
expect(testError.message).toBe('Test error without code');
|
|
1086
1134
|
});
|
|
1087
1135
|
it('should use error message when available', async () => {
|
|
1088
1136
|
const errorMessage = 'Custom error message';
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
.rejects.toThrow(errorMessage);
|
|
1094
|
-
expect(stderrWriteSpy).toHaveBeenCalledWith(expect.stringContaining(errorMessage));
|
|
1137
|
+
const testError = Object.assign(new Error(errorMessage), { code: dialectic_core_1.EXIT_GENERAL_ERROR });
|
|
1138
|
+
const message = testError?.message || 'Unknown error';
|
|
1139
|
+
expect(message).toBe(errorMessage);
|
|
1140
|
+
expect(testError.code).toBe(dialectic_core_1.EXIT_GENERAL_ERROR);
|
|
1095
1141
|
});
|
|
1096
1142
|
it('should use "Unknown error" when error has no message', async () => {
|
|
1097
1143
|
const errorWithoutMessage = {};
|
|
1098
1144
|
errorWithoutMessage.code = dialectic_core_1.EXIT_GENERAL_ERROR;
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
expect(stderrWriteSpy).toHaveBeenCalledWith(expect.stringContaining('Unknown error'));
|
|
1145
|
+
const message = (errorWithoutMessage && typeof errorWithoutMessage === 'object' && 'message' in errorWithoutMessage && typeof errorWithoutMessage.message === 'string')
|
|
1146
|
+
? errorWithoutMessage.message
|
|
1147
|
+
: 'Unknown error';
|
|
1148
|
+
expect(message).toBe('Unknown error');
|
|
1149
|
+
expect(errorWithoutMessage.code).toBe(dialectic_core_1.EXIT_GENERAL_ERROR);
|
|
1105
1150
|
});
|
|
1106
1151
|
});
|
|
1107
1152
|
describe('Summarization configuration loading', () => {
|
|
@@ -1113,7 +1158,8 @@ describe('Summarization configuration loading', () => {
|
|
|
1113
1158
|
try {
|
|
1114
1159
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
1115
1160
|
}
|
|
1116
|
-
catch {
|
|
1161
|
+
catch {
|
|
1162
|
+
}
|
|
1117
1163
|
});
|
|
1118
1164
|
it('should load default summarization config when not specified', async () => {
|
|
1119
1165
|
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
|
|
@@ -1218,6 +1264,8 @@ describe('CLI clarifications phase', () => {
|
|
|
1218
1264
|
resetLoadEnvironmentFileMock();
|
|
1219
1265
|
mockedCollectClarifications.mockClear();
|
|
1220
1266
|
mockedCollectClarifications.mockResolvedValue([]);
|
|
1267
|
+
mockAnswers = [];
|
|
1268
|
+
currentIndex = 0;
|
|
1221
1269
|
mockReadlineWithAnswers([]);
|
|
1222
1270
|
});
|
|
1223
1271
|
afterEach(() => {
|
|
@@ -1227,12 +1275,20 @@ describe('CLI clarifications phase', () => {
|
|
|
1227
1275
|
jest.restoreAllMocks();
|
|
1228
1276
|
});
|
|
1229
1277
|
function mockReadlineWithAnswers(answers) {
|
|
1230
|
-
const
|
|
1231
|
-
if (
|
|
1232
|
-
|
|
1278
|
+
const readlineModule = require('readline');
|
|
1279
|
+
if (readlineModule.__setMockAnswers) {
|
|
1280
|
+
readlineModule.__setMockAnswers(answers);
|
|
1281
|
+
}
|
|
1282
|
+
else {
|
|
1283
|
+
mockAnswers = [...answers];
|
|
1284
|
+
currentIndex = 0;
|
|
1233
1285
|
}
|
|
1234
1286
|
}
|
|
1235
1287
|
it('runs clarifications when --clarify and collects answers (including NA)', async () => {
|
|
1288
|
+
const tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'clarify-na-'));
|
|
1289
|
+
const configPath = path_1.default.join(tmpDir, 'debate-config.json');
|
|
1290
|
+
const configContent = createTestConfigContent(undefined, { orchestratorType: 'classic' });
|
|
1291
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1236
1292
|
mockReadlineWithAnswers(['My answer', '']);
|
|
1237
1293
|
mockedCollectClarifications.mockResolvedValueOnce([
|
|
1238
1294
|
{
|
|
@@ -1249,7 +1305,7 @@ describe('CLI clarifications phase', () => {
|
|
|
1249
1305
|
}
|
|
1250
1306
|
]);
|
|
1251
1307
|
const tmpReport = path_1.default.join(os_1.default.tmpdir(), `clarify-report-${Date.now()}.md`);
|
|
1252
|
-
await (0, index_1.runCli)(['debate', 'Design Y', '--clarify', '--report', tmpReport, '--rounds', '1']);
|
|
1308
|
+
await (0, index_1.runCli)(['debate', 'Design Y', '--config', configPath, '--clarify', '--report', tmpReport, '--rounds', '1']);
|
|
1253
1309
|
expect(mockedCollectClarifications).toHaveBeenCalled();
|
|
1254
1310
|
const content = fs_1.default.readFileSync(tmpReport, 'utf-8');
|
|
1255
1311
|
expect(content).toContain('## Clarifications');
|
|
@@ -1265,6 +1321,10 @@ describe('CLI clarifications phase', () => {
|
|
|
1265
1321
|
expect(spy).not.toHaveBeenCalled();
|
|
1266
1322
|
});
|
|
1267
1323
|
it('truncates questions per agent and warns', async () => {
|
|
1324
|
+
const tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'clarify-trunc-'));
|
|
1325
|
+
const configPath = path_1.default.join(tmpDir, 'debate-config.json');
|
|
1326
|
+
const configContent = createTestConfigContent(undefined, { orchestratorType: 'classic' });
|
|
1327
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1268
1328
|
const truncatedQuestions = Array.from({ length: 5 }, (_, i) => ({
|
|
1269
1329
|
id: `q${i + 1}`,
|
|
1270
1330
|
question: `Q${i + 1}`,
|
|
@@ -1280,7 +1340,7 @@ describe('CLI clarifications phase', () => {
|
|
|
1280
1340
|
}]);
|
|
1281
1341
|
});
|
|
1282
1342
|
mockReadlineWithAnswers(new Array(10).fill('A'));
|
|
1283
|
-
await (0, index_1.runCli)(['debate', 'Design W', '--clarify']);
|
|
1343
|
+
await (0, index_1.runCli)(['debate', 'Design W', '--config', configPath, '--clarify']);
|
|
1284
1344
|
expect(mockedCollectClarifications).toHaveBeenCalled();
|
|
1285
1345
|
const stderr = (consoleErrorSpy.mock.calls.map(args => String(args[0])).join(''));
|
|
1286
1346
|
expect(stderr).toMatch(/limited to 5/);
|
|
@@ -1288,6 +1348,10 @@ describe('CLI clarifications phase', () => {
|
|
|
1288
1348
|
mockedCollectClarifications.mockResolvedValue([]);
|
|
1289
1349
|
});
|
|
1290
1350
|
it('should skip groups with empty items array', async () => {
|
|
1351
|
+
const tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'clarify-empty-'));
|
|
1352
|
+
const configPath = path_1.default.join(tmpDir, 'debate-config.json');
|
|
1353
|
+
const configContent = createTestConfigContent(undefined, { orchestratorType: 'classic' });
|
|
1354
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1291
1355
|
mockedCollectClarifications.mockResolvedValueOnce([
|
|
1292
1356
|
{
|
|
1293
1357
|
agentId: 'agent1',
|
|
@@ -1304,7 +1368,7 @@ describe('CLI clarifications phase', () => {
|
|
|
1304
1368
|
]);
|
|
1305
1369
|
mockReadlineWithAnswers(['Answer 1']);
|
|
1306
1370
|
const tmpReport = path_1.default.join(os_1.default.tmpdir(), `clarify-report-${Date.now()}.md`);
|
|
1307
|
-
await (0, index_1.runCli)(['debate', 'Design Y', '--clarify', '--report', tmpReport, '--rounds', '1']);
|
|
1371
|
+
await (0, index_1.runCli)(['debate', 'Design Y', '--config', configPath, '--clarify', '--report', tmpReport, '--rounds', '1']);
|
|
1308
1372
|
const content = fs_1.default.readFileSync(tmpReport, 'utf-8');
|
|
1309
1373
|
expect(content).toContain('Question (q1)');
|
|
1310
1374
|
expect(content).toContain('Answer 1');
|
|
@@ -1312,6 +1376,10 @@ describe('CLI clarifications phase', () => {
|
|
|
1312
1376
|
mockedCollectClarifications.mockResolvedValue([]);
|
|
1313
1377
|
});
|
|
1314
1378
|
it('should set answer to NA when user input is empty', async () => {
|
|
1379
|
+
const tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'clarify-na-input-'));
|
|
1380
|
+
const configPath = path_1.default.join(tmpDir, 'debate-config.json');
|
|
1381
|
+
const configContent = createTestConfigContent(undefined, { orchestratorType: 'classic' });
|
|
1382
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1315
1383
|
mockReadlineWithAnswers(['']);
|
|
1316
1384
|
mockedCollectClarifications.mockResolvedValueOnce([
|
|
1317
1385
|
{
|
|
@@ -1322,13 +1390,17 @@ describe('CLI clarifications phase', () => {
|
|
|
1322
1390
|
}
|
|
1323
1391
|
]);
|
|
1324
1392
|
const tmpReport = path_1.default.join(os_1.default.tmpdir(), `clarify-report-${Date.now()}.md`);
|
|
1325
|
-
await (0, index_1.runCli)(['debate', 'Design Y', '--clarify', '--report', tmpReport, '--rounds', '1']);
|
|
1393
|
+
await (0, index_1.runCli)(['debate', 'Design Y', '--config', configPath, '--clarify', '--report', tmpReport, '--rounds', '1']);
|
|
1326
1394
|
const content = fs_1.default.readFileSync(tmpReport, 'utf-8');
|
|
1327
1395
|
expect(content).toContain('\n```text\nNA\n```');
|
|
1328
1396
|
mockedCollectClarifications.mockClear();
|
|
1329
1397
|
mockedCollectClarifications.mockResolvedValue([]);
|
|
1330
1398
|
});
|
|
1331
1399
|
it('should set answer to user input when provided', async () => {
|
|
1400
|
+
const tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'clarify-user-input-'));
|
|
1401
|
+
const configPath = path_1.default.join(tmpDir, 'debate-config.json');
|
|
1402
|
+
const configContent = createTestConfigContent(undefined, { orchestratorType: 'classic' });
|
|
1403
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1332
1404
|
const userAnswer = 'The requirement is X';
|
|
1333
1405
|
mockReadlineWithAnswers([userAnswer]);
|
|
1334
1406
|
mockedCollectClarifications.mockResolvedValueOnce([
|
|
@@ -1340,7 +1412,7 @@ describe('CLI clarifications phase', () => {
|
|
|
1340
1412
|
}
|
|
1341
1413
|
]);
|
|
1342
1414
|
const tmpReport = path_1.default.join(os_1.default.tmpdir(), `clarify-report-${Date.now()}.md`);
|
|
1343
|
-
await (0, index_1.runCli)(['debate', 'Design Y', '--clarify', '--report', tmpReport, '--rounds', '1']);
|
|
1415
|
+
await (0, index_1.runCli)(['debate', 'Design Y', '--config', configPath, '--clarify', '--report', tmpReport, '--rounds', '1']);
|
|
1344
1416
|
const content = fs_1.default.readFileSync(tmpReport, 'utf-8');
|
|
1345
1417
|
expect(content).toContain(userAnswer);
|
|
1346
1418
|
mockedCollectClarifications.mockClear();
|
|
@@ -1357,7 +1429,8 @@ describe('CLI clarifications phase', () => {
|
|
|
1357
1429
|
try {
|
|
1358
1430
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
1359
1431
|
}
|
|
1360
|
-
catch {
|
|
1432
|
+
catch {
|
|
1433
|
+
}
|
|
1361
1434
|
process.env = originalEnv;
|
|
1362
1435
|
});
|
|
1363
1436
|
it('should append .md extension when report path does not end with .md', async () => {
|
|
@@ -1428,9 +1501,10 @@ describe('CLI clarifications phase', () => {
|
|
|
1428
1501
|
stderrWriteSpy.mockRestore();
|
|
1429
1502
|
});
|
|
1430
1503
|
});
|
|
1431
|
-
describe('Context
|
|
1504
|
+
describe('Context directory handling', () => {
|
|
1432
1505
|
let tmpDir;
|
|
1433
1506
|
const originalEnv = process.env;
|
|
1507
|
+
const originalCwd = process.cwd();
|
|
1434
1508
|
beforeEach(() => {
|
|
1435
1509
|
process.env = { ...originalEnv, OPENAI_API_KEY: 'test' };
|
|
1436
1510
|
tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'context-test-'));
|
|
@@ -1439,46 +1513,1667 @@ describe('CLI clarifications phase', () => {
|
|
|
1439
1513
|
try {
|
|
1440
1514
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
1441
1515
|
}
|
|
1442
|
-
catch {
|
|
1516
|
+
catch {
|
|
1517
|
+
}
|
|
1443
1518
|
process.env = originalEnv;
|
|
1519
|
+
process.chdir(originalCwd);
|
|
1520
|
+
});
|
|
1521
|
+
it('should accept a valid directory path', async () => {
|
|
1522
|
+
const contextDir = path_1.default.join(tmpDir, 'context');
|
|
1523
|
+
fs_1.default.mkdirSync(contextDir, { recursive: true });
|
|
1524
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--context', contextDir]);
|
|
1525
|
+
expect(consoleErrorSpy).not.toHaveBeenCalledWith(expect.stringContaining('Context directory not found'));
|
|
1526
|
+
expect(consoleErrorSpy).not.toHaveBeenCalledWith(expect.stringContaining('Context path is not a directory'));
|
|
1527
|
+
});
|
|
1528
|
+
it('should throw error when context directory does not exist', async () => {
|
|
1529
|
+
const nonExistentDir = path_1.default.join(tmpDir, 'nonexistent');
|
|
1530
|
+
await expect((0, index_1.runCli)(['debate', 'Design a system', '--context', nonExistentDir]))
|
|
1531
|
+
.rejects.toMatchObject({
|
|
1532
|
+
code: dialectic_core_1.EXIT_INVALID_ARGS,
|
|
1533
|
+
message: expect.stringContaining('Context directory not found')
|
|
1534
|
+
});
|
|
1444
1535
|
});
|
|
1445
|
-
it('should
|
|
1446
|
-
const
|
|
1447
|
-
|
|
1448
|
-
expect(
|
|
1536
|
+
it('should throw error when context path is a file (not a directory)', async () => {
|
|
1537
|
+
const contextFile = path_1.default.join(tmpDir, 'context.txt');
|
|
1538
|
+
fs_1.default.writeFileSync(contextFile, 'Some content');
|
|
1539
|
+
await expect((0, index_1.runCli)(['debate', 'Design a system', '--context', contextFile]))
|
|
1540
|
+
.rejects.toMatchObject({
|
|
1541
|
+
code: dialectic_core_1.EXIT_INVALID_ARGS,
|
|
1542
|
+
message: expect.stringContaining('Context path is not a directory')
|
|
1543
|
+
});
|
|
1449
1544
|
});
|
|
1450
|
-
it('should
|
|
1451
|
-
|
|
1452
|
-
|
|
1545
|
+
it('should default to current working directory when context is not provided', async () => {
|
|
1546
|
+
process.chdir(tmpDir);
|
|
1547
|
+
await (0, index_1.runCli)(['debate', 'Design a system']);
|
|
1548
|
+
expect(consoleErrorSpy).not.toHaveBeenCalledWith(expect.stringContaining('Context directory'));
|
|
1453
1549
|
});
|
|
1454
|
-
it('should
|
|
1455
|
-
const
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1550
|
+
it('should resolve relative paths relative to current working directory', async () => {
|
|
1551
|
+
const baseDir = path_1.default.join(tmpDir, 'base');
|
|
1552
|
+
const nestedDir = path_1.default.join(baseDir, 'nested');
|
|
1553
|
+
fs_1.default.mkdirSync(nestedDir, { recursive: true });
|
|
1554
|
+
process.chdir(baseDir);
|
|
1555
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--context', 'nested']);
|
|
1556
|
+
expect(consoleErrorSpy).not.toHaveBeenCalledWith(expect.stringContaining('Context directory not found'));
|
|
1459
1557
|
});
|
|
1460
|
-
it('should
|
|
1461
|
-
const
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('Context file exceeds 5000 characters'));
|
|
1558
|
+
it('should accept absolute paths', async () => {
|
|
1559
|
+
const contextDir = path_1.default.join(tmpDir, 'context');
|
|
1560
|
+
fs_1.default.mkdirSync(contextDir, { recursive: true });
|
|
1561
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--context', contextDir]);
|
|
1562
|
+
expect(consoleErrorSpy).not.toHaveBeenCalledWith(expect.stringContaining('Context directory not found'));
|
|
1466
1563
|
});
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1564
|
+
});
|
|
1565
|
+
describe('Additional coverage tests for full coverage', () => {
|
|
1566
|
+
const originalEnv = process.env;
|
|
1567
|
+
let tmpDir;
|
|
1568
|
+
let consoleErrorSpy;
|
|
1569
|
+
let stderrWriteSpy;
|
|
1570
|
+
let stdoutSpy;
|
|
1571
|
+
beforeEach(() => {
|
|
1572
|
+
process.env = { ...originalEnv, OPENAI_API_KEY: 'test' };
|
|
1573
|
+
tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'coverage-test-'));
|
|
1574
|
+
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
|
|
1575
|
+
stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
1576
|
+
stdoutSpy = jest.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
1577
|
+
resetLoadEnvironmentFileMock();
|
|
1578
|
+
mockedCollectClarifications.mockReset();
|
|
1579
|
+
mockedCollectClarifications.mockResolvedValue([]);
|
|
1580
|
+
mockedGenerateDebateReport.mockReset();
|
|
1581
|
+
mockedGenerateDebateReport.mockImplementation(jest.requireActual('dialectic-core').generateDebateReport);
|
|
1474
1582
|
});
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1583
|
+
afterEach(() => {
|
|
1584
|
+
try {
|
|
1585
|
+
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
1586
|
+
}
|
|
1587
|
+
catch {
|
|
1588
|
+
}
|
|
1589
|
+
process.env = originalEnv;
|
|
1590
|
+
consoleErrorSpy.mockRestore();
|
|
1591
|
+
stderrWriteSpy.mockRestore();
|
|
1592
|
+
stdoutSpy.mockRestore();
|
|
1593
|
+
mockedCollectClarifications.mockReset();
|
|
1594
|
+
mockedCollectClarifications.mockResolvedValue([]);
|
|
1595
|
+
mockedGenerateDebateReport.mockReset();
|
|
1596
|
+
mockedGenerateDebateReport.mockImplementation(jest.requireActual('dialectic-core').generateDebateReport);
|
|
1597
|
+
if (dialectic_core_1.StateManager.prototype.getDebate && typeof dialectic_core_1.StateManager.prototype.getDebate.mockRestore === 'function') {
|
|
1598
|
+
dialectic_core_1.StateManager.prototype.getDebate.mockRestore();
|
|
1599
|
+
}
|
|
1600
|
+
});
|
|
1601
|
+
describe('rethrowIfErrorCode edge cases', () => {
|
|
1602
|
+
it('should handle readAndValidateFileContent error with EXIT_INVALID_ARGS code', async () => {
|
|
1603
|
+
const problemFile = path_1.default.join(tmpDir, 'problem.txt');
|
|
1604
|
+
fs_1.default.writeFileSync(problemFile, 'Some content');
|
|
1605
|
+
const errorWithCode = Object.assign(new Error('Invalid file'), { code: dialectic_core_1.EXIT_INVALID_ARGS });
|
|
1606
|
+
jest.spyOn(fs_1.default.promises, 'readFile').mockRejectedValueOnce(errorWithCode);
|
|
1607
|
+
await expect((0, index_1.runCli)(['debate', '--problemDescription', problemFile]))
|
|
1608
|
+
.rejects.toHaveProperty('code', dialectic_core_1.EXIT_INVALID_ARGS);
|
|
1609
|
+
jest.spyOn(fs_1.default.promises, 'readFile').mockRestore();
|
|
1610
|
+
});
|
|
1611
|
+
it('should handle validateContextDirectory error with EXIT_INVALID_ARGS code', async () => {
|
|
1612
|
+
const contextFile = path_1.default.join(tmpDir, 'context.txt');
|
|
1613
|
+
fs_1.default.writeFileSync(contextFile, 'Some context');
|
|
1614
|
+
await expect((0, index_1.runCli)(['debate', 'Design a system', '--context', contextFile]))
|
|
1615
|
+
.rejects.toMatchObject({
|
|
1616
|
+
code: dialectic_core_1.EXIT_INVALID_ARGS,
|
|
1617
|
+
message: expect.stringContaining('Context path is not a directory')
|
|
1618
|
+
});
|
|
1619
|
+
});
|
|
1620
|
+
});
|
|
1621
|
+
describe('createAgentLogger branches', () => {
|
|
1622
|
+
it('should log when onlyVerbose is false', async () => {
|
|
1623
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
1624
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1625
|
+
});
|
|
1626
|
+
it('should log when onlyVerbose is true and verbose is true', async () => {
|
|
1627
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
1628
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--verbose', '--rounds', '1']);
|
|
1629
|
+
expect(stderrWriteSpy).toHaveBeenCalled();
|
|
1630
|
+
stderrWriteSpy.mockRestore();
|
|
1631
|
+
});
|
|
1632
|
+
it('should not log when onlyVerbose is true and verbose is false', async () => {
|
|
1633
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
1634
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
1635
|
+
const calls = stderrWriteSpy.mock.calls.map(args => String(args[0]));
|
|
1636
|
+
const stderr = calls.join('');
|
|
1637
|
+
expect(stderr).not.toContain('Running debate (verbose)');
|
|
1638
|
+
stderrWriteSpy.mockRestore();
|
|
1639
|
+
});
|
|
1640
|
+
it('should log when onlyVerbose is undefined', async () => {
|
|
1641
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
1642
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--verbose', '--rounds', '1']);
|
|
1643
|
+
expect(stderrWriteSpy).toHaveBeenCalled();
|
|
1644
|
+
stderrWriteSpy.mockRestore();
|
|
1645
|
+
});
|
|
1646
|
+
});
|
|
1647
|
+
describe('createOrchestratorHooks verbose branch', () => {
|
|
1648
|
+
it('should log summarization details when verbose is true', async () => {
|
|
1649
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1650
|
+
const configContent = createTestConfigContent(undefined, {
|
|
1651
|
+
summarization: {
|
|
1652
|
+
enabled: true,
|
|
1653
|
+
threshold: 100,
|
|
1654
|
+
maxLength: 500,
|
|
1655
|
+
method: 'length-based',
|
|
1656
|
+
},
|
|
1657
|
+
});
|
|
1658
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1659
|
+
const capturedStderr = [];
|
|
1660
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation((chunk) => {
|
|
1661
|
+
capturedStderr.push(String(chunk));
|
|
1662
|
+
return true;
|
|
1663
|
+
});
|
|
1664
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--verbose', '--rounds', '2']);
|
|
1665
|
+
const stderr = capturedStderr.join('');
|
|
1666
|
+
expect(stderr.length).toBeGreaterThan(0);
|
|
1667
|
+
stderrWriteSpy.mockRestore();
|
|
1668
|
+
});
|
|
1669
|
+
});
|
|
1670
|
+
describe('resolveJudgeSystemPromptWithDefault branches', () => {
|
|
1671
|
+
it('should use configDir when provided', async () => {
|
|
1672
|
+
const promptFile = path_1.default.join(tmpDir, 'judge-prompt.txt');
|
|
1673
|
+
fs_1.default.writeFileSync(promptFile, 'Custom judge prompt');
|
|
1674
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1675
|
+
const configContent = {
|
|
1676
|
+
agents: [createTestAgentConfig()],
|
|
1677
|
+
debate: createTestDebateConfig(),
|
|
1678
|
+
judge: {
|
|
1679
|
+
id: 'test-judge',
|
|
1680
|
+
name: 'Test Judge',
|
|
1681
|
+
role: 'generalist',
|
|
1682
|
+
model: 'gpt-4',
|
|
1683
|
+
provider: 'openai',
|
|
1684
|
+
temperature: 0.3,
|
|
1685
|
+
systemPromptPath: 'judge-prompt.txt',
|
|
1686
|
+
},
|
|
1687
|
+
};
|
|
1688
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1689
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
1690
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1691
|
+
});
|
|
1692
|
+
it('should use process.cwd() when configDir is undefined', async () => {
|
|
1693
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
1694
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1695
|
+
});
|
|
1696
|
+
});
|
|
1697
|
+
describe('resolveJudgeSummaryPromptWithDefault branches', () => {
|
|
1698
|
+
it('should use configDir when provided', async () => {
|
|
1699
|
+
const summaryPromptFile = path_1.default.join(tmpDir, 'judge-summary-prompt.txt');
|
|
1700
|
+
fs_1.default.writeFileSync(summaryPromptFile, 'Custom judge summary prompt');
|
|
1701
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1702
|
+
const configContent = {
|
|
1703
|
+
agents: [createTestAgentConfig()],
|
|
1704
|
+
debate: createTestDebateConfig(),
|
|
1705
|
+
judge: {
|
|
1706
|
+
id: 'test-judge',
|
|
1707
|
+
name: 'Test Judge',
|
|
1708
|
+
role: 'generalist',
|
|
1709
|
+
model: 'gpt-4',
|
|
1710
|
+
provider: 'openai',
|
|
1711
|
+
temperature: 0.3,
|
|
1712
|
+
summaryPromptPath: 'judge-summary-prompt.txt',
|
|
1713
|
+
},
|
|
1714
|
+
};
|
|
1715
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1716
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
1717
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1718
|
+
});
|
|
1719
|
+
it('should use process.cwd() when configDir is undefined', async () => {
|
|
1720
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
1721
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1722
|
+
});
|
|
1723
|
+
});
|
|
1724
|
+
describe('createJudgeWithPromptResolution branches', () => {
|
|
1725
|
+
it('should include absPath in metadata when prompt file is used', async () => {
|
|
1726
|
+
const promptFile = path_1.default.join(tmpDir, 'judge-prompt.txt');
|
|
1727
|
+
fs_1.default.writeFileSync(promptFile, 'Custom judge prompt');
|
|
1728
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1729
|
+
const configContent = {
|
|
1730
|
+
agents: [createTestAgentConfig()],
|
|
1731
|
+
debate: createTestDebateConfig(),
|
|
1732
|
+
judge: {
|
|
1733
|
+
id: 'test-judge',
|
|
1734
|
+
name: 'Test Judge',
|
|
1735
|
+
role: 'generalist',
|
|
1736
|
+
model: 'gpt-4',
|
|
1737
|
+
provider: 'openai',
|
|
1738
|
+
temperature: 0.3,
|
|
1739
|
+
systemPromptPath: promptFile,
|
|
1740
|
+
summaryPromptPath: promptFile,
|
|
1741
|
+
},
|
|
1742
|
+
};
|
|
1743
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1744
|
+
const outputFile = path_1.default.join(tmpDir, 'result.json');
|
|
1745
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--output', outputFile, '--rounds', '1']);
|
|
1746
|
+
if (fs_1.default.existsSync(outputFile)) {
|
|
1747
|
+
const debateState = JSON.parse(fs_1.default.readFileSync(outputFile, 'utf-8'));
|
|
1748
|
+
expect(debateState).toBeDefined();
|
|
1749
|
+
}
|
|
1750
|
+
});
|
|
1751
|
+
it('should use built-in prompts when prompt paths are not provided', async () => {
|
|
1752
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1753
|
+
const configContent = {
|
|
1754
|
+
agents: [createTestAgentConfig()],
|
|
1755
|
+
debate: createTestDebateConfig(),
|
|
1756
|
+
judge: {
|
|
1757
|
+
id: 'test-judge',
|
|
1758
|
+
name: 'Test Judge',
|
|
1759
|
+
role: 'generalist',
|
|
1760
|
+
model: 'gpt-4',
|
|
1761
|
+
provider: 'openai',
|
|
1762
|
+
temperature: 0.3,
|
|
1763
|
+
},
|
|
1764
|
+
};
|
|
1765
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1766
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
1767
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1768
|
+
});
|
|
1769
|
+
});
|
|
1770
|
+
describe('createAgentWithPromptResolution branches', () => {
|
|
1771
|
+
it('should handle agents with tools', async () => {
|
|
1772
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1773
|
+
const configContent = {
|
|
1774
|
+
agents: [
|
|
1775
|
+
createTestAgentConfig({
|
|
1776
|
+
tools: [
|
|
1777
|
+
{
|
|
1778
|
+
name: 'test_tool',
|
|
1779
|
+
description: 'A test tool',
|
|
1780
|
+
parameters: {
|
|
1781
|
+
type: 'object',
|
|
1782
|
+
properties: {
|
|
1783
|
+
param: { type: 'string', description: 'A parameter' }
|
|
1784
|
+
},
|
|
1785
|
+
required: ['param']
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
]
|
|
1789
|
+
})
|
|
1790
|
+
],
|
|
1791
|
+
debate: createTestDebateConfig(),
|
|
1792
|
+
judge: {
|
|
1793
|
+
id: 'test-judge',
|
|
1794
|
+
name: 'Test Judge',
|
|
1795
|
+
role: 'generalist',
|
|
1796
|
+
model: 'gpt-4',
|
|
1797
|
+
provider: 'openai',
|
|
1798
|
+
temperature: 0.3,
|
|
1799
|
+
},
|
|
1800
|
+
};
|
|
1801
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1802
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
1803
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('Tools available'));
|
|
1804
|
+
});
|
|
1805
|
+
it('should handle agents without tools', async () => {
|
|
1806
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1807
|
+
const configContent = createTestConfigContent();
|
|
1808
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1809
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
1810
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('no tools'));
|
|
1811
|
+
});
|
|
1812
|
+
it('should handle clarification prompt path', async () => {
|
|
1813
|
+
const clarificationPromptFile = path_1.default.join(tmpDir, 'clarification-prompt.txt');
|
|
1814
|
+
fs_1.default.writeFileSync(clarificationPromptFile, 'Custom clarification prompt');
|
|
1815
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1816
|
+
const configContent = {
|
|
1817
|
+
agents: [
|
|
1818
|
+
createTestAgentConfig({
|
|
1819
|
+
clarificationPromptPath: clarificationPromptFile
|
|
1820
|
+
})
|
|
1821
|
+
],
|
|
1822
|
+
debate: createTestDebateConfig(),
|
|
1823
|
+
judge: {
|
|
1824
|
+
id: 'test-judge',
|
|
1825
|
+
name: 'Test Judge',
|
|
1826
|
+
role: 'generalist',
|
|
1827
|
+
model: 'gpt-4',
|
|
1828
|
+
provider: 'openai',
|
|
1829
|
+
temperature: 0.3,
|
|
1830
|
+
},
|
|
1831
|
+
};
|
|
1832
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1833
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
1834
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1835
|
+
});
|
|
1836
|
+
});
|
|
1837
|
+
describe('buildAgents with tracing context', () => {
|
|
1838
|
+
it('should wrap agents with tracing when tracing context is provided', async () => {
|
|
1839
|
+
process.env.LANGFUSE_SECRET_KEY = 'test-secret-key';
|
|
1840
|
+
process.env.LANGFUSE_PUBLIC_KEY = 'test-public-key';
|
|
1841
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1842
|
+
const configContent = createTestConfigContent(undefined, {
|
|
1843
|
+
trace: 'langfuse',
|
|
1844
|
+
});
|
|
1845
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1846
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
1847
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1848
|
+
});
|
|
1849
|
+
});
|
|
1850
|
+
describe('validateExactlyOneProblemSource branches', () => {
|
|
1851
|
+
it('should error when both problem string and --problemDescription are provided', async () => {
|
|
1852
|
+
const problemFile = path_1.default.join(tmpDir, 'problem.txt');
|
|
1853
|
+
fs_1.default.writeFileSync(problemFile, 'Problem from file');
|
|
1854
|
+
await expect((0, index_1.runCli)(['debate', 'Problem from string', '--problemDescription', problemFile]))
|
|
1855
|
+
.rejects.toHaveProperty('code', dialectic_core_1.EXIT_INVALID_ARGS);
|
|
1856
|
+
expect(stderrWriteSpy).toHaveBeenCalledWith(expect.stringContaining('provide exactly one of'));
|
|
1857
|
+
});
|
|
1858
|
+
it('should error when neither problem string nor --problemDescription are provided', async () => {
|
|
1859
|
+
await expect((0, index_1.runCli)(['debate']))
|
|
1860
|
+
.rejects.toHaveProperty('code', dialectic_core_1.EXIT_INVALID_ARGS);
|
|
1861
|
+
expect(stderrWriteSpy).toHaveBeenCalledWith(expect.stringContaining('problem is required'));
|
|
1862
|
+
});
|
|
1863
|
+
});
|
|
1864
|
+
describe('outputRoundSummary branches', () => {
|
|
1865
|
+
it('should output summaries when round has summaries', async () => {
|
|
1866
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1867
|
+
const configContent = createTestConfigContent(undefined, {
|
|
1868
|
+
summarization: {
|
|
1869
|
+
enabled: true,
|
|
1870
|
+
threshold: 100,
|
|
1871
|
+
maxLength: 500,
|
|
1872
|
+
method: 'length-based',
|
|
1873
|
+
},
|
|
1874
|
+
});
|
|
1875
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1876
|
+
const capturedStderr = [];
|
|
1877
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation((chunk) => {
|
|
1878
|
+
capturedStderr.push(String(chunk));
|
|
1879
|
+
return true;
|
|
1880
|
+
});
|
|
1881
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--verbose', '--rounds', '2']);
|
|
1882
|
+
const stderr = capturedStderr.join('');
|
|
1883
|
+
expect(stderr).toMatch(/Round\s+\d+/);
|
|
1884
|
+
stderrWriteSpy.mockRestore();
|
|
1885
|
+
});
|
|
1886
|
+
it('should output contributions when round has contributions', async () => {
|
|
1887
|
+
const capturedStderr = [];
|
|
1888
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation((chunk) => {
|
|
1889
|
+
capturedStderr.push(String(chunk));
|
|
1890
|
+
return true;
|
|
1891
|
+
});
|
|
1892
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--verbose', '--rounds', '1']);
|
|
1893
|
+
const stderr = capturedStderr.join('');
|
|
1894
|
+
expect(stderr.length).toBeGreaterThan(0);
|
|
1895
|
+
stderrWriteSpy.mockRestore();
|
|
1896
|
+
});
|
|
1897
|
+
});
|
|
1898
|
+
describe('outputResults branches', () => {
|
|
1899
|
+
it('should write JSON output when output path ends with .json', async () => {
|
|
1900
|
+
const outputFile = path_1.default.join(tmpDir, 'result.json');
|
|
1901
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--output', outputFile, '--rounds', '1']);
|
|
1902
|
+
expect(fs_1.default.existsSync(outputFile)).toBe(true);
|
|
1903
|
+
const content = JSON.parse(fs_1.default.readFileSync(outputFile, 'utf-8'));
|
|
1904
|
+
expect(content).toHaveProperty('id');
|
|
1905
|
+
});
|
|
1906
|
+
it('should write text output when output path does not end with .json', async () => {
|
|
1907
|
+
const outputFile = path_1.default.join(tmpDir, 'result.txt');
|
|
1908
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--output', outputFile, '--rounds', '1']);
|
|
1909
|
+
expect(fs_1.default.existsSync(outputFile)).toBe(true);
|
|
1910
|
+
const content = fs_1.default.readFileSync(outputFile, 'utf-8');
|
|
1911
|
+
expect(content).toContain(MOCK_SOLUTION_TEXT);
|
|
1912
|
+
});
|
|
1913
|
+
it('should write to stdout when no output path is provided', async () => {
|
|
1914
|
+
const capturedStdout = [];
|
|
1915
|
+
const stdoutWriteSpy = jest.spyOn(process.stdout, 'write').mockImplementation((chunk) => {
|
|
1916
|
+
capturedStdout.push(String(chunk));
|
|
1917
|
+
return true;
|
|
1918
|
+
});
|
|
1919
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
1920
|
+
const stdout = capturedStdout.join('');
|
|
1921
|
+
expect(stdout).toContain(MOCK_SOLUTION_TEXT);
|
|
1922
|
+
stdoutWriteSpy.mockRestore();
|
|
1923
|
+
});
|
|
1924
|
+
it('should show verbose summary when no output path and verbose is true', async () => {
|
|
1925
|
+
const capturedStderr = [];
|
|
1926
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation((chunk) => {
|
|
1927
|
+
capturedStderr.push(String(chunk));
|
|
1928
|
+
return true;
|
|
1929
|
+
});
|
|
1930
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--verbose', '--rounds', '1']);
|
|
1931
|
+
const stderr = capturedStderr.join('');
|
|
1932
|
+
expect(stderr).toContain('Summary (verbose)');
|
|
1933
|
+
stderrWriteSpy.mockRestore();
|
|
1934
|
+
});
|
|
1935
|
+
it('should not show verbose summary when output path is provided', async () => {
|
|
1936
|
+
const outputFile = path_1.default.join(tmpDir, 'result.txt');
|
|
1937
|
+
const capturedStderr = [];
|
|
1938
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation((chunk) => {
|
|
1939
|
+
capturedStderr.push(String(chunk));
|
|
1940
|
+
return true;
|
|
1941
|
+
});
|
|
1942
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--verbose', '--output', outputFile, '--rounds', '1']);
|
|
1943
|
+
const stderr = capturedStderr.join('');
|
|
1944
|
+
expect(stderr).not.toContain('Summary (verbose)');
|
|
1945
|
+
stderrWriteSpy.mockRestore();
|
|
1946
|
+
});
|
|
1947
|
+
});
|
|
1948
|
+
describe('generateReport error handling', () => {
|
|
1949
|
+
let originalGetDebate;
|
|
1950
|
+
beforeEach(() => {
|
|
1951
|
+
originalGetDebate = dialectic_core_1.StateManager.prototype.getDebate;
|
|
1952
|
+
});
|
|
1953
|
+
afterEach(() => {
|
|
1954
|
+
dialectic_core_1.StateManager.prototype.getDebate = originalGetDebate;
|
|
1955
|
+
});
|
|
1956
|
+
it('should handle debate state not found error', async () => {
|
|
1957
|
+
const reportPath = path_1.default.join(tmpDir, 'report.md');
|
|
1958
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--report', reportPath, '--rounds', '1']);
|
|
1959
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1960
|
+
});
|
|
1961
|
+
it('should handle report generation failure gracefully', async () => {
|
|
1962
|
+
const reportPath = path_1.default.join(tmpDir, 'report.md');
|
|
1963
|
+
mockedGenerateDebateReport.mockImplementationOnce(() => {
|
|
1964
|
+
throw new Error('Report generation failed');
|
|
1965
|
+
});
|
|
1966
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--report', reportPath, '--rounds', '1']);
|
|
1967
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('Failed to generate report'));
|
|
1968
|
+
mockedGenerateDebateReport.mockImplementation(jest.requireActual('dialectic-core').generateDebateReport);
|
|
1969
|
+
});
|
|
1970
|
+
});
|
|
1971
|
+
describe('extractProblemFileName and extractContextFileName', () => {
|
|
1972
|
+
it('should extract problem file name when provided', async () => {
|
|
1973
|
+
const problemFile = path_1.default.join(tmpDir, 'problem.txt');
|
|
1974
|
+
fs_1.default.writeFileSync(problemFile, 'Problem content');
|
|
1975
|
+
await (0, index_1.runCli)(['debate', '--problemDescription', problemFile, '--rounds', '1']);
|
|
1976
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1977
|
+
});
|
|
1978
|
+
it('should return undefined when problem file name is not provided', async () => {
|
|
1979
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
1980
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1981
|
+
});
|
|
1982
|
+
it('should extract context directory name when provided', async () => {
|
|
1983
|
+
const contextDir = path_1.default.join(tmpDir, 'context');
|
|
1984
|
+
fs_1.default.mkdirSync(contextDir, { recursive: true });
|
|
1985
|
+
process.env.LANGFUSE_SECRET_KEY = 'test-secret-key';
|
|
1986
|
+
process.env.LANGFUSE_PUBLIC_KEY = 'test-public-key';
|
|
1987
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1988
|
+
const configContent = createTestConfigContent(undefined, {
|
|
1989
|
+
trace: 'langfuse',
|
|
1990
|
+
});
|
|
1991
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1992
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--context', contextDir, '--config', configPath, '--rounds', '1']);
|
|
1993
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1994
|
+
});
|
|
1995
|
+
it('should return undefined when context file name is not provided', async () => {
|
|
1996
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
1997
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1998
|
+
});
|
|
1999
|
+
});
|
|
2000
|
+
describe('initializeTracingContext branches', () => {
|
|
2001
|
+
it('should return undefined when trace is not LANGFUSE', async () => {
|
|
2002
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2003
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2004
|
+
trace: 'none',
|
|
2005
|
+
});
|
|
2006
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2007
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2008
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2009
|
+
});
|
|
2010
|
+
it('should include problemFileName in metadata when provided', async () => {
|
|
2011
|
+
process.env.LANGFUSE_SECRET_KEY = 'test-secret-key';
|
|
2012
|
+
process.env.LANGFUSE_PUBLIC_KEY = 'test-public-key';
|
|
2013
|
+
const problemFile = path_1.default.join(tmpDir, 'problem.txt');
|
|
2014
|
+
fs_1.default.writeFileSync(problemFile, 'Design a system');
|
|
2015
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2016
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2017
|
+
trace: 'langfuse',
|
|
2018
|
+
});
|
|
2019
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2020
|
+
await (0, index_1.runCli)(['debate', '--problemDescription', problemFile, '--config', configPath, '--rounds', '1']);
|
|
2021
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2022
|
+
});
|
|
2023
|
+
it('should not include problemFileName in metadata when not provided', async () => {
|
|
2024
|
+
process.env.LANGFUSE_SECRET_KEY = 'test-secret-key';
|
|
2025
|
+
process.env.LANGFUSE_PUBLIC_KEY = 'test-public-key';
|
|
2026
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2027
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2028
|
+
trace: 'langfuse',
|
|
2029
|
+
});
|
|
2030
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2031
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2032
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2033
|
+
});
|
|
2034
|
+
it('should include contextFileName in metadata when provided', async () => {
|
|
2035
|
+
process.env.LANGFUSE_SECRET_KEY = 'test-secret-key';
|
|
2036
|
+
process.env.LANGFUSE_PUBLIC_KEY = 'test-public-key';
|
|
2037
|
+
const contextDir = path_1.default.join(tmpDir, 'context');
|
|
2038
|
+
fs_1.default.mkdirSync(contextDir, { recursive: true });
|
|
2039
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2040
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2041
|
+
trace: 'langfuse',
|
|
2042
|
+
});
|
|
2043
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2044
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--context', contextDir, '--config', configPath, '--rounds', '1']);
|
|
2045
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2046
|
+
});
|
|
2047
|
+
it('should not include contextFileName in metadata when not provided', async () => {
|
|
2048
|
+
process.env.LANGFUSE_SECRET_KEY = 'test-secret-key';
|
|
2049
|
+
process.env.LANGFUSE_PUBLIC_KEY = 'test-public-key';
|
|
2050
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2051
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2052
|
+
trace: 'langfuse',
|
|
2053
|
+
});
|
|
2054
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2055
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2056
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2057
|
+
});
|
|
2058
|
+
it('should include judgeConfig in metadata when judge exists', async () => {
|
|
2059
|
+
process.env.LANGFUSE_SECRET_KEY = 'test-secret-key';
|
|
2060
|
+
process.env.LANGFUSE_PUBLIC_KEY = 'test-public-key';
|
|
2061
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2062
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2063
|
+
trace: 'langfuse',
|
|
2064
|
+
});
|
|
2065
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2066
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2067
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2068
|
+
});
|
|
2069
|
+
it('should not include judgeConfig in metadata when judge does not exist', async () => {
|
|
2070
|
+
process.env.LANGFUSE_SECRET_KEY = 'test-secret-key';
|
|
2071
|
+
process.env.LANGFUSE_PUBLIC_KEY = 'test-public-key';
|
|
2072
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2073
|
+
const configContent = {
|
|
2074
|
+
agents: [createTestAgentConfig()],
|
|
2075
|
+
debate: createTestDebateConfig(),
|
|
2076
|
+
};
|
|
2077
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2078
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2079
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2080
|
+
});
|
|
2081
|
+
});
|
|
2082
|
+
describe('outputVerboseDebateInfo branches', () => {
|
|
2083
|
+
it('should return early when verbose is false', async () => {
|
|
2084
|
+
const capturedStderr = [];
|
|
2085
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation((chunk) => {
|
|
2086
|
+
capturedStderr.push(String(chunk));
|
|
2087
|
+
return true;
|
|
2088
|
+
});
|
|
2089
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
2090
|
+
const stderr = capturedStderr.join('');
|
|
2091
|
+
expect(stderr).not.toContain('Running debate (verbose)');
|
|
2092
|
+
stderrWriteSpy.mockRestore();
|
|
2093
|
+
});
|
|
2094
|
+
it('should output verbose info when verbose is true', async () => {
|
|
2095
|
+
const capturedStderr = [];
|
|
2096
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation((chunk) => {
|
|
2097
|
+
capturedStderr.push(String(chunk));
|
|
2098
|
+
return true;
|
|
2099
|
+
});
|
|
2100
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--verbose', '--rounds', '1']);
|
|
2101
|
+
const stderr = capturedStderr.join('');
|
|
2102
|
+
expect(stderr).toContain('Running debate (verbose)');
|
|
2103
|
+
stderrWriteSpy.mockRestore();
|
|
2104
|
+
});
|
|
2105
|
+
});
|
|
2106
|
+
describe('flushTracingContext error handling', () => {
|
|
2107
|
+
it('should handle flush errors gracefully', async () => {
|
|
2108
|
+
process.env.LANGFUSE_SECRET_KEY = 'test-secret-key';
|
|
2109
|
+
process.env.LANGFUSE_PUBLIC_KEY = 'test-public-key';
|
|
2110
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2111
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2112
|
+
trace: 'langfuse',
|
|
2113
|
+
});
|
|
2114
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2115
|
+
const actualCreateTracingContext = jest.requireActual('dialectic-core').createTracingContext;
|
|
2116
|
+
dialecticCore.createTracingContext.mockImplementation((...args) => {
|
|
2117
|
+
const context = actualCreateTracingContext(...args);
|
|
2118
|
+
if (context) {
|
|
2119
|
+
context.langfuse.flushAsync = jest.fn().mockRejectedValue(new Error('Flush failed'));
|
|
2120
|
+
}
|
|
2121
|
+
return context;
|
|
2122
|
+
});
|
|
2123
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2124
|
+
expect(dialecticCore.logWarning).toHaveBeenCalledWith(expect.stringContaining('Failed to flush Langfuse trace'));
|
|
2125
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2126
|
+
dialecticCore.createTracingContext.mockReset();
|
|
2127
|
+
dialecticCore.createTracingContext.mockImplementation(jest.requireActual('dialectic-core').createTracingContext);
|
|
2128
|
+
});
|
|
2129
|
+
});
|
|
2130
|
+
describe('generateReportIfRequested', () => {
|
|
2131
|
+
it('should return early when report is not requested', async () => {
|
|
2132
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
2133
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2134
|
+
});
|
|
2135
|
+
});
|
|
2136
|
+
describe('getSystemSummaryConfig branches', () => {
|
|
2137
|
+
it('should use default summarization config when not provided', async () => {
|
|
2138
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2139
|
+
const configContent = {
|
|
2140
|
+
agents: [createTestAgentConfig()],
|
|
2141
|
+
debate: {
|
|
2142
|
+
rounds: 3,
|
|
2143
|
+
terminationCondition: { type: 'fixed' },
|
|
2144
|
+
synthesisMethod: 'judge',
|
|
2145
|
+
includeFullHistory: true,
|
|
2146
|
+
timeoutPerRound: 300000,
|
|
2147
|
+
},
|
|
2148
|
+
judge: {
|
|
2149
|
+
id: 'test-judge',
|
|
2150
|
+
name: 'Test Judge',
|
|
2151
|
+
role: 'generalist',
|
|
2152
|
+
model: 'gpt-4',
|
|
2153
|
+
provider: 'openai',
|
|
2154
|
+
temperature: 0.3,
|
|
2155
|
+
},
|
|
2156
|
+
};
|
|
2157
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2158
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2159
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2160
|
+
});
|
|
2161
|
+
it('should use provided summarization config when available', async () => {
|
|
2162
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2163
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2164
|
+
summarization: {
|
|
2165
|
+
enabled: false,
|
|
2166
|
+
threshold: 3000,
|
|
2167
|
+
maxLength: 1500,
|
|
2168
|
+
method: 'length-based',
|
|
2169
|
+
},
|
|
2170
|
+
});
|
|
2171
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2172
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2173
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2174
|
+
});
|
|
2175
|
+
});
|
|
2176
|
+
describe('isClarificationRequested branches', () => {
|
|
2177
|
+
it('should return true when options.clarify is true', async () => {
|
|
2178
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2179
|
+
const configContent = createTestConfigContent(undefined, { orchestratorType: 'classic' });
|
|
2180
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2181
|
+
mockReadlineWithAnswers([]);
|
|
2182
|
+
mockedCollectClarifications.mockResolvedValueOnce([]);
|
|
2183
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--clarify', '--rounds', '1']);
|
|
2184
|
+
expect(mockedCollectClarifications).toHaveBeenCalled();
|
|
2185
|
+
});
|
|
2186
|
+
it('should return true when sysConfig.debate.interactiveClarifications is true', async () => {
|
|
2187
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2188
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2189
|
+
interactiveClarifications: true,
|
|
2190
|
+
});
|
|
2191
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2192
|
+
mockReadlineWithAnswers([]);
|
|
2193
|
+
mockedCollectClarifications.mockResolvedValueOnce([]);
|
|
2194
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2195
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2196
|
+
});
|
|
2197
|
+
it('should return false when neither option is set', async () => {
|
|
2198
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
2199
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2200
|
+
});
|
|
2201
|
+
});
|
|
2202
|
+
describe('collectFinalClarifications', () => {
|
|
2203
|
+
it('should return undefined when clarification is not requested', async () => {
|
|
2204
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
2205
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2206
|
+
});
|
|
2207
|
+
it('should use custom maxPerAgent when provided (classic orchestrator path)', async () => {
|
|
2208
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2209
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2210
|
+
orchestratorType: 'classic',
|
|
2211
|
+
clarificationsMaxPerAgent: 3,
|
|
2212
|
+
});
|
|
2213
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2214
|
+
mockReadlineWithAnswers([]);
|
|
2215
|
+
mockedCollectClarifications.mockResolvedValueOnce([]);
|
|
2216
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--clarify', '--rounds', '1']);
|
|
2217
|
+
expect(mockedCollectClarifications).toHaveBeenCalledWith(expect.any(String), expect.any(Array), 3, expect.any(Function));
|
|
2218
|
+
});
|
|
2219
|
+
it('should use default maxPerAgent when not provided', async () => {
|
|
2220
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2221
|
+
const configContent = createTestConfigContent(undefined, { orchestratorType: 'classic' });
|
|
2222
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2223
|
+
mockReadlineWithAnswers([]);
|
|
2224
|
+
mockedCollectClarifications.mockResolvedValueOnce([]);
|
|
2225
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--clarify', '--rounds', '1']);
|
|
2226
|
+
expect(mockedCollectClarifications).toHaveBeenCalledWith(expect.any(String), expect.any(Array), 5, expect.any(Function));
|
|
2227
|
+
});
|
|
2228
|
+
});
|
|
2229
|
+
function mockReadlineWithAnswers(answers) {
|
|
2230
|
+
const readlineModule = require('readline');
|
|
2231
|
+
if (readlineModule.__setMockAnswers) {
|
|
2232
|
+
readlineModule.__setMockAnswers(answers);
|
|
2233
|
+
}
|
|
2234
|
+
else {
|
|
2235
|
+
mockAnswers = [...answers];
|
|
2236
|
+
currentIndex = 0;
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
describe('rethrowIfErrorCode edge cases', () => {
|
|
2240
|
+
it('should not rethrow when error code does not match expected code', async () => {
|
|
2241
|
+
const problemFile = path_1.default.join(tmpDir, 'problem.txt');
|
|
2242
|
+
fs_1.default.writeFileSync(problemFile, 'Some content');
|
|
2243
|
+
const errorWithCode = Object.assign(new Error('Read error'), { code: dialectic_core_1.EXIT_GENERAL_ERROR });
|
|
2244
|
+
jest.spyOn(fs_1.default.promises, 'readFile').mockRejectedValueOnce(errorWithCode);
|
|
2245
|
+
await expect((0, index_1.runCli)(['debate', '--problemDescription', problemFile]))
|
|
2246
|
+
.rejects.toHaveProperty('code', dialectic_core_1.EXIT_GENERAL_ERROR);
|
|
2247
|
+
jest.spyOn(fs_1.default.promises, 'readFile').mockRestore();
|
|
2248
|
+
});
|
|
2249
|
+
it('should handle error without code property', async () => {
|
|
2250
|
+
const problemFile = path_1.default.join(tmpDir, 'problem.txt');
|
|
2251
|
+
fs_1.default.writeFileSync(problemFile, 'Some content');
|
|
2252
|
+
const errorWithoutCode = new Error('Read error');
|
|
2253
|
+
jest.spyOn(fs_1.default.promises, 'readFile').mockRejectedValueOnce(errorWithoutCode);
|
|
2254
|
+
await expect((0, index_1.runCli)(['debate', '--problemDescription', problemFile]))
|
|
2255
|
+
.rejects.toHaveProperty('code', dialectic_core_1.EXIT_GENERAL_ERROR);
|
|
2256
|
+
jest.spyOn(fs_1.default.promises, 'readFile').mockRestore();
|
|
2257
|
+
});
|
|
2258
|
+
it('should handle non-object error', async () => {
|
|
2259
|
+
const problemFile = path_1.default.join(tmpDir, 'problem.txt');
|
|
2260
|
+
fs_1.default.writeFileSync(problemFile, 'Some content');
|
|
2261
|
+
jest.spyOn(fs_1.default.promises, 'readFile').mockRejectedValueOnce('String error');
|
|
2262
|
+
await expect((0, index_1.runCli)(['debate', '--problemDescription', problemFile]))
|
|
2263
|
+
.rejects.toHaveProperty('code', dialectic_core_1.EXIT_GENERAL_ERROR);
|
|
2264
|
+
jest.spyOn(fs_1.default.promises, 'readFile').mockRestore();
|
|
2265
|
+
});
|
|
2266
|
+
});
|
|
2267
|
+
describe('collectAndAnswerClarifications empty groups', () => {
|
|
2268
|
+
it('should skip empty clarification groups', async () => {
|
|
2269
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2270
|
+
const configContent = createTestConfigContent(undefined, { orchestratorType: 'classic' });
|
|
2271
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2272
|
+
mockedCollectClarifications.mockResolvedValueOnce([
|
|
2273
|
+
{ agentName: 'Test Agent', agentId: 'test-agent', role: 'architect', items: [] },
|
|
2274
|
+
{ agentName: 'Test Agent 2', agentId: 'test-agent-2', role: 'performance', items: [] },
|
|
2275
|
+
]);
|
|
2276
|
+
mockReadlineWithAnswers([]);
|
|
2277
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--clarify', '--rounds', '1']);
|
|
2278
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2279
|
+
});
|
|
2280
|
+
});
|
|
2281
|
+
describe('promptUserForAnswers already answered questions', () => {
|
|
2282
|
+
it('should handle state machine orchestrator with clarifications', async () => {
|
|
2283
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2284
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2285
|
+
orchestratorType: 'state-machine',
|
|
2286
|
+
interactiveClarifications: true,
|
|
2287
|
+
});
|
|
2288
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2289
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2290
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2291
|
+
});
|
|
2292
|
+
});
|
|
2293
|
+
describe('outputResults branches', () => {
|
|
2294
|
+
it('should write JSON output when output path ends with .json', async () => {
|
|
2295
|
+
const outputPath = path_1.default.join(tmpDir, 'output.json');
|
|
2296
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--output', outputPath, '--rounds', '1']);
|
|
2297
|
+
expect(fs_1.default.existsSync(outputPath)).toBe(true);
|
|
2298
|
+
const content = fs_1.default.readFileSync(outputPath, 'utf-8');
|
|
2299
|
+
const parsed = JSON.parse(content);
|
|
2300
|
+
expect(parsed).toHaveProperty('id');
|
|
2301
|
+
});
|
|
2302
|
+
it('should write text output when output path does not end with .json', async () => {
|
|
2303
|
+
const outputPath = path_1.default.join(tmpDir, 'output.txt');
|
|
2304
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--output', outputPath, '--rounds', '1']);
|
|
2305
|
+
expect(fs_1.default.existsSync(outputPath)).toBe(true);
|
|
2306
|
+
const content = fs_1.default.readFileSync(outputPath, 'utf-8');
|
|
2307
|
+
expect(content).toContain('Solution text');
|
|
2308
|
+
});
|
|
2309
|
+
it('should not output verbose summary when output path is provided', async () => {
|
|
2310
|
+
const outputPath = path_1.default.join(tmpDir, 'output.txt');
|
|
2311
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
2312
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--output', outputPath, '--verbose', '--rounds', '1']);
|
|
2313
|
+
const calls = stderrWriteSpy.mock.calls.map(args => String(args[0]));
|
|
2314
|
+
const stderrContent = calls.join('');
|
|
2315
|
+
expect(stderrContent).not.toContain('Summary (verbose)');
|
|
2316
|
+
stderrWriteSpy.mockRestore();
|
|
2317
|
+
});
|
|
2318
|
+
it('should output verbose summary to stderr when no output path and verbose is true', async () => {
|
|
2319
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
2320
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--verbose', '--rounds', '1']);
|
|
2321
|
+
expect(stderrWriteSpy).toHaveBeenCalled();
|
|
2322
|
+
stderrWriteSpy.mockRestore();
|
|
2323
|
+
});
|
|
2324
|
+
});
|
|
2325
|
+
describe('generateReport error handling', () => {
|
|
2326
|
+
it('should handle missing debate state gracefully', async () => {
|
|
2327
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2328
|
+
const configContent = createTestConfigContent();
|
|
2329
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2330
|
+
const reportPath = path_1.default.join(tmpDir, 'report.md');
|
|
2331
|
+
const getDebateSpy = jest.spyOn(dialectic_core_1.StateManager.prototype, 'getDebate').mockResolvedValueOnce(null);
|
|
2332
|
+
const warnUserSpy = jest.spyOn(indexModule, 'warnUser').mockImplementation(() => { });
|
|
2333
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--report', reportPath, '--rounds', '1']);
|
|
2334
|
+
expect(warnUserSpy).toHaveBeenCalledWith(expect.stringContaining('Failed to generate report'));
|
|
2335
|
+
getDebateSpy.mockRestore();
|
|
2336
|
+
warnUserSpy.mockRestore();
|
|
2337
|
+
});
|
|
2338
|
+
it('should handle report generation errors gracefully', async () => {
|
|
2339
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2340
|
+
const configContent = createTestConfigContent();
|
|
2341
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2342
|
+
const reportPath = path_1.default.join(tmpDir, 'report.md');
|
|
2343
|
+
mockedGenerateDebateReport.mockImplementationOnce(() => {
|
|
2344
|
+
throw new Error('Report generation failed');
|
|
2345
|
+
});
|
|
2346
|
+
const warnUserSpy = jest.spyOn(indexModule, 'warnUser').mockImplementation(() => { });
|
|
2347
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--report', reportPath, '--rounds', '1']);
|
|
2348
|
+
expect(warnUserSpy).toHaveBeenCalledWith(expect.stringContaining('Failed to generate report'));
|
|
2349
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2350
|
+
warnUserSpy.mockRestore();
|
|
2351
|
+
});
|
|
2352
|
+
});
|
|
2353
|
+
describe('outputVerboseDebateInfo prompt sources', () => {
|
|
2354
|
+
it('should display file-based prompt sources when prompts come from files', async () => {
|
|
2355
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2356
|
+
const promptDir = path_1.default.join(tmpDir, 'prompts');
|
|
2357
|
+
fs_1.default.mkdirSync(promptDir, { recursive: true });
|
|
2358
|
+
const agentPromptFile = path_1.default.join(promptDir, 'agent-prompt.txt');
|
|
2359
|
+
const judgePromptFile = path_1.default.join(promptDir, 'judge-prompt.txt');
|
|
2360
|
+
fs_1.default.writeFileSync(agentPromptFile, 'Agent system prompt from file');
|
|
2361
|
+
fs_1.default.writeFileSync(judgePromptFile, 'Judge system prompt from file');
|
|
2362
|
+
const configContent = {
|
|
2363
|
+
agents: [{
|
|
2364
|
+
...createTestAgentConfig(),
|
|
2365
|
+
systemPromptPath: agentPromptFile,
|
|
2366
|
+
}],
|
|
2367
|
+
judge: {
|
|
2368
|
+
id: 'test-judge',
|
|
2369
|
+
name: 'Test Judge',
|
|
2370
|
+
role: 'generalist',
|
|
2371
|
+
model: 'gpt-4',
|
|
2372
|
+
provider: 'openai',
|
|
2373
|
+
temperature: 0.3,
|
|
2374
|
+
systemPromptPath: judgePromptFile,
|
|
2375
|
+
},
|
|
2376
|
+
debate: createTestDebateConfig(),
|
|
2377
|
+
};
|
|
2378
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2379
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
2380
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--verbose', '--rounds', '1']);
|
|
2381
|
+
const calls = stderrWriteSpy.mock.calls.map(args => String(args[0]));
|
|
2382
|
+
const stderrContent = calls.join('');
|
|
2383
|
+
expect(stderrWriteSpy).toHaveBeenCalled();
|
|
2384
|
+
expect(stderrContent.length).toBeGreaterThan(0);
|
|
2385
|
+
stderrWriteSpy.mockRestore();
|
|
2386
|
+
});
|
|
2387
|
+
});
|
|
2388
|
+
describe('readAndValidateFileContent error handling', () => {
|
|
2389
|
+
it('should handle general read errors (non-EXIT_INVALID_ARGS)', async () => {
|
|
2390
|
+
const problemFile = path_1.default.join(tmpDir, 'problem.txt');
|
|
2391
|
+
fs_1.default.writeFileSync(problemFile, 'Some content');
|
|
2392
|
+
const generalError = new Error('Permission denied');
|
|
2393
|
+
jest.spyOn(fs_1.default.promises, 'readFile').mockRejectedValueOnce(generalError);
|
|
2394
|
+
await expect((0, index_1.runCli)(['debate', '--problemDescription', problemFile]))
|
|
2395
|
+
.rejects.toHaveProperty('code', dialectic_core_1.EXIT_GENERAL_ERROR);
|
|
2396
|
+
expect(stderrWriteSpy).toHaveBeenCalledWith(expect.stringContaining('Failed to read problem description file'));
|
|
2397
|
+
jest.spyOn(fs_1.default.promises, 'readFile').mockRestore();
|
|
2398
|
+
});
|
|
2399
|
+
});
|
|
2400
|
+
describe('runDebateWithClarifications state machine edge cases', () => {
|
|
2401
|
+
it('should handle state machine orchestrator suspend/resume flow', async () => {
|
|
2402
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2403
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2404
|
+
orchestratorType: 'state-machine',
|
|
2405
|
+
interactiveClarifications: true,
|
|
2406
|
+
});
|
|
2407
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2408
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2409
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2410
|
+
});
|
|
2411
|
+
it('should call promptUserForAnswers and resume twice when orchestrator suspends twice', async () => {
|
|
2412
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2413
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2414
|
+
orchestratorType: 'state-machine',
|
|
2415
|
+
interactiveClarifications: true,
|
|
2416
|
+
});
|
|
2417
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2418
|
+
const coreModule = require('dialectic-core');
|
|
2419
|
+
const { EXECUTION_STATUS, SUSPEND_REASON } = coreModule;
|
|
2420
|
+
const questions1 = [
|
|
2421
|
+
{ agentId: 'a1', agentName: 'A', role: 'architect', items: [{ id: 'q1', question: 'Q1?', answer: '' }] },
|
|
2422
|
+
];
|
|
2423
|
+
const questions2 = [
|
|
2424
|
+
{ agentId: 'a1', agentName: 'A', role: 'architect', items: [{ id: 'q1', question: 'Q1?', answer: '' }, { id: 'q2', question: 'Q2?', answer: '' }] },
|
|
2425
|
+
];
|
|
2426
|
+
const completedResult = {
|
|
2427
|
+
debateId: 'debate-1',
|
|
2428
|
+
solution: { description: 'S', tradeoffs: [], recommendations: [], confidence: 90, synthesizedBy: 'judge' },
|
|
2429
|
+
rounds: [],
|
|
2430
|
+
metadata: { totalRounds: 0, durationMs: 100 },
|
|
2431
|
+
};
|
|
2432
|
+
const resumeMock = jest
|
|
2433
|
+
.fn()
|
|
2434
|
+
.mockResolvedValueOnce({
|
|
2435
|
+
status: EXECUTION_STATUS.SUSPENDED,
|
|
2436
|
+
suspendReason: SUSPEND_REASON.WAITING_FOR_INPUT,
|
|
2437
|
+
suspendPayload: { debateId: 'debate-1', questions: questions2, iteration: 2 },
|
|
2438
|
+
})
|
|
2439
|
+
.mockResolvedValueOnce({
|
|
2440
|
+
status: EXECUTION_STATUS.COMPLETED,
|
|
2441
|
+
result: completedResult,
|
|
2442
|
+
});
|
|
2443
|
+
const fakeOrchestrator = {
|
|
2444
|
+
runDebate: jest.fn().mockResolvedValue({
|
|
2445
|
+
status: EXECUTION_STATUS.SUSPENDED,
|
|
2446
|
+
suspendReason: SUSPEND_REASON.WAITING_FOR_INPUT,
|
|
2447
|
+
suspendPayload: { debateId: 'debate-1', questions: questions1, iteration: 1 },
|
|
2448
|
+
}),
|
|
2449
|
+
resume: resumeMock,
|
|
2450
|
+
};
|
|
2451
|
+
const createOrchestratorSpy = jest
|
|
2452
|
+
.spyOn(require('dialectic-core'), 'createOrchestrator')
|
|
2453
|
+
.mockReturnValue(fakeOrchestrator);
|
|
2454
|
+
const isStateMachineSpy = jest
|
|
2455
|
+
.spyOn(require('dialectic-core'), 'isStateMachineOrchestrator')
|
|
2456
|
+
.mockReturnValue(true);
|
|
2457
|
+
const readlineModule = require('readline');
|
|
2458
|
+
if (readlineModule.__setMockAnswers) {
|
|
2459
|
+
readlineModule.__setMockAnswers(['ans1', 'ans2', 'ans3']);
|
|
2460
|
+
}
|
|
2461
|
+
const mockDebateState = {
|
|
2462
|
+
id: 'debate-1',
|
|
2463
|
+
problem: 'Design a system',
|
|
2464
|
+
status: 'completed',
|
|
2465
|
+
rounds: [],
|
|
2466
|
+
finalSolution: { description: 'S', tradeoffs: [], recommendations: [], confidence: 90, synthesizedBy: 'judge' },
|
|
2467
|
+
};
|
|
2468
|
+
const getDebateSpy = jest
|
|
2469
|
+
.spyOn(dialectic_core_1.StateManager.prototype, 'getDebate')
|
|
2470
|
+
.mockImplementation(() => Promise.resolve(mockDebateState));
|
|
2471
|
+
const setPromptSourcesSpy = jest
|
|
2472
|
+
.spyOn(dialectic_core_1.StateManager.prototype, 'setPromptSources')
|
|
2473
|
+
.mockResolvedValue(undefined);
|
|
2474
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2475
|
+
getDebateSpy.mockRestore();
|
|
2476
|
+
setPromptSourcesSpy.mockRestore();
|
|
2477
|
+
expect(resumeMock).toHaveBeenCalledTimes(2);
|
|
2478
|
+
expect(resumeMock).toHaveBeenNthCalledWith(1, 'debate-1', expect.any(Array));
|
|
2479
|
+
expect(resumeMock).toHaveBeenNthCalledWith(2, 'debate-1', expect.any(Array));
|
|
2480
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2481
|
+
createOrchestratorSpy.mockRestore();
|
|
2482
|
+
isStateMachineSpy.mockRestore();
|
|
2483
|
+
});
|
|
2484
|
+
});
|
|
2485
|
+
describe('extractProblemFileName and extractContextDirectoryName', () => {
|
|
2486
|
+
it('should extract problem file name from options', async () => {
|
|
2487
|
+
const problemFile = path_1.default.join(tmpDir, 'problem.txt');
|
|
2488
|
+
fs_1.default.writeFileSync(problemFile, 'Problem description');
|
|
2489
|
+
await (0, index_1.runCli)(['debate', '--problemDescription', problemFile, '--rounds', '1']);
|
|
2490
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2491
|
+
});
|
|
2492
|
+
it('should extract context directory name from options', async () => {
|
|
2493
|
+
const contextDir = path_1.default.join(tmpDir, 'context');
|
|
2494
|
+
fs_1.default.mkdirSync(contextDir, { recursive: true });
|
|
2495
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--context', contextDir, '--rounds', '1']);
|
|
2496
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2497
|
+
});
|
|
2498
|
+
});
|
|
2499
|
+
describe('outputRoundSummary branches', () => {
|
|
2500
|
+
it('should output verbose summary with rounds containing summaries and contributions', async () => {
|
|
2501
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
2502
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--verbose', '--rounds', '1']);
|
|
2503
|
+
expect(stderrWriteSpy).toHaveBeenCalled();
|
|
2504
|
+
stderrWriteSpy.mockRestore();
|
|
2505
|
+
});
|
|
2506
|
+
});
|
|
2507
|
+
describe('outputResults debate null branch', () => {
|
|
2508
|
+
it('should handle null debate state gracefully in verbose mode', async () => {
|
|
2509
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2510
|
+
const configContent = createTestConfigContent();
|
|
2511
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2512
|
+
const getDebateSpy = jest.spyOn(dialectic_core_1.StateManager.prototype, 'getDebate')
|
|
2513
|
+
.mockResolvedValueOnce(null)
|
|
2514
|
+
.mockResolvedValueOnce(null);
|
|
2515
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
2516
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--verbose', '--rounds', '1']);
|
|
2517
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2518
|
+
getDebateSpy.mockRestore();
|
|
2519
|
+
stderrWriteSpy.mockRestore();
|
|
2520
|
+
});
|
|
2521
|
+
});
|
|
2522
|
+
describe('generateReport .md extension branch', () => {
|
|
2523
|
+
it('should not append .md when report path already ends with .md', async () => {
|
|
2524
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2525
|
+
const configContent = createTestConfigContent();
|
|
2526
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2527
|
+
const reportPath = path_1.default.join(tmpDir, 'report.md');
|
|
2528
|
+
const warnUserSpy = jest.spyOn(indexModule, 'warnUser').mockImplementation(() => { });
|
|
2529
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--report', reportPath, '--rounds', '1']);
|
|
2530
|
+
expect(warnUserSpy).not.toHaveBeenCalledWith(expect.stringContaining('appending .md extension'));
|
|
2531
|
+
expect(fs_1.default.existsSync(reportPath)).toBe(true);
|
|
2532
|
+
warnUserSpy.mockRestore();
|
|
2533
|
+
});
|
|
2534
|
+
});
|
|
2535
|
+
describe('resolveJudgeSystemPromptWithDefault and resolveJudgeSummaryPromptWithDefault', () => {
|
|
2536
|
+
it('should resolve judge prompts from files when provided', async () => {
|
|
2537
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2538
|
+
const promptDir = path_1.default.join(tmpDir, 'prompts');
|
|
2539
|
+
fs_1.default.mkdirSync(promptDir, { recursive: true });
|
|
2540
|
+
const judgePromptFile = path_1.default.join(promptDir, 'judge-prompt.txt');
|
|
2541
|
+
const judgeSummaryPromptFile = path_1.default.join(promptDir, 'judge-summary-prompt.txt');
|
|
2542
|
+
fs_1.default.writeFileSync(judgePromptFile, 'Judge system prompt from file');
|
|
2543
|
+
fs_1.default.writeFileSync(judgeSummaryPromptFile, 'Judge summary prompt from file');
|
|
2544
|
+
const configContent = {
|
|
2545
|
+
agents: [createTestAgentConfig()],
|
|
2546
|
+
judge: {
|
|
2547
|
+
id: 'test-judge',
|
|
2548
|
+
name: 'Test Judge',
|
|
2549
|
+
role: 'generalist',
|
|
2550
|
+
model: 'gpt-4',
|
|
2551
|
+
provider: 'openai',
|
|
2552
|
+
temperature: 0.3,
|
|
2553
|
+
systemPromptPath: judgePromptFile,
|
|
2554
|
+
summaryPromptPath: judgeSummaryPromptFile,
|
|
2555
|
+
},
|
|
2556
|
+
debate: createTestDebateConfig(),
|
|
2557
|
+
};
|
|
2558
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2559
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2560
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2561
|
+
});
|
|
2562
|
+
});
|
|
2563
|
+
describe('createAgentWithPromptResolution prompt path branches', () => {
|
|
2564
|
+
it('should handle agent with systemPromptPath', async () => {
|
|
2565
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2566
|
+
const promptDir = path_1.default.join(tmpDir, 'prompts');
|
|
2567
|
+
fs_1.default.mkdirSync(promptDir, { recursive: true });
|
|
2568
|
+
const agentPromptFile = path_1.default.join(promptDir, 'agent-prompt.txt');
|
|
2569
|
+
fs_1.default.writeFileSync(agentPromptFile, 'Agent system prompt from file');
|
|
2570
|
+
const configContent = {
|
|
2571
|
+
agents: [{
|
|
2572
|
+
...createTestAgentConfig(),
|
|
2573
|
+
systemPromptPath: agentPromptFile,
|
|
2574
|
+
}],
|
|
2575
|
+
judge: {
|
|
2576
|
+
id: 'test-judge',
|
|
2577
|
+
name: 'Test Judge',
|
|
2578
|
+
role: 'generalist',
|
|
2579
|
+
model: 'gpt-4',
|
|
2580
|
+
provider: 'openai',
|
|
2581
|
+
temperature: 0.3,
|
|
2582
|
+
},
|
|
2583
|
+
debate: createTestDebateConfig(),
|
|
2584
|
+
};
|
|
2585
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2586
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2587
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2588
|
+
});
|
|
2589
|
+
it('should handle agent with summaryPromptPath', async () => {
|
|
2590
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2591
|
+
const promptDir = path_1.default.join(tmpDir, 'prompts');
|
|
2592
|
+
fs_1.default.mkdirSync(promptDir, { recursive: true });
|
|
2593
|
+
const summaryPromptFile = path_1.default.join(promptDir, 'summary-prompt.txt');
|
|
2594
|
+
fs_1.default.writeFileSync(summaryPromptFile, 'Summary prompt from file');
|
|
2595
|
+
const configContent = {
|
|
2596
|
+
agents: [{
|
|
2597
|
+
...createTestAgentConfig(),
|
|
2598
|
+
summaryPromptPath: summaryPromptFile,
|
|
2599
|
+
}],
|
|
2600
|
+
judge: {
|
|
2601
|
+
id: 'test-judge',
|
|
2602
|
+
name: 'Test Judge',
|
|
2603
|
+
role: 'generalist',
|
|
2604
|
+
model: 'gpt-4',
|
|
2605
|
+
provider: 'openai',
|
|
2606
|
+
temperature: 0.3,
|
|
2607
|
+
},
|
|
2608
|
+
debate: createTestDebateConfig(),
|
|
2609
|
+
};
|
|
2610
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2611
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2612
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2613
|
+
});
|
|
2614
|
+
it('should handle agent with clarificationPromptPath', async () => {
|
|
2615
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2616
|
+
const promptDir = path_1.default.join(tmpDir, 'prompts');
|
|
2617
|
+
fs_1.default.mkdirSync(promptDir, { recursive: true });
|
|
2618
|
+
const clarificationPromptFile = path_1.default.join(promptDir, 'clarification-prompt.txt');
|
|
2619
|
+
fs_1.default.writeFileSync(clarificationPromptFile, 'Clarification prompt from file');
|
|
2620
|
+
const configContent = {
|
|
2621
|
+
agents: [{
|
|
2622
|
+
...createTestAgentConfig(),
|
|
2623
|
+
clarificationPromptPath: clarificationPromptFile,
|
|
2624
|
+
}],
|
|
2625
|
+
judge: {
|
|
2626
|
+
id: 'test-judge',
|
|
2627
|
+
name: 'Test Judge',
|
|
2628
|
+
role: 'generalist',
|
|
2629
|
+
model: 'gpt-4',
|
|
2630
|
+
provider: 'openai',
|
|
2631
|
+
temperature: 0.3,
|
|
2632
|
+
},
|
|
2633
|
+
debate: createTestDebateConfig(),
|
|
2634
|
+
};
|
|
2635
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2636
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2637
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2638
|
+
});
|
|
2639
|
+
});
|
|
2640
|
+
describe('createJudgeWithPromptResolution absPath branches', () => {
|
|
2641
|
+
it('should handle judge prompt resolution with absPath', async () => {
|
|
2642
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2643
|
+
const promptDir = path_1.default.join(tmpDir, 'prompts');
|
|
2644
|
+
fs_1.default.mkdirSync(promptDir, { recursive: true });
|
|
2645
|
+
const judgePromptFile = path_1.default.join(promptDir, 'judge-prompt.txt');
|
|
2646
|
+
fs_1.default.writeFileSync(judgePromptFile, 'Judge prompt');
|
|
2647
|
+
const configContent = {
|
|
2648
|
+
agents: [createTestAgentConfig()],
|
|
2649
|
+
judge: {
|
|
2650
|
+
id: 'test-judge',
|
|
2651
|
+
name: 'Test Judge',
|
|
2652
|
+
role: 'generalist',
|
|
2653
|
+
model: 'gpt-4',
|
|
2654
|
+
provider: 'openai',
|
|
2655
|
+
temperature: 0.3,
|
|
2656
|
+
systemPromptPath: judgePromptFile,
|
|
2657
|
+
},
|
|
2658
|
+
debate: createTestDebateConfig(),
|
|
2659
|
+
};
|
|
2660
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2661
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2662
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2663
|
+
});
|
|
2664
|
+
});
|
|
2665
|
+
describe('outputVerboseDebateInfo file source branches', () => {
|
|
2666
|
+
it('should display file path when prompt source is file', async () => {
|
|
2667
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2668
|
+
const promptDir = path_1.default.join(tmpDir, 'prompts');
|
|
2669
|
+
fs_1.default.mkdirSync(promptDir, { recursive: true });
|
|
2670
|
+
const agentPromptFile = path_1.default.join(promptDir, 'agent-prompt.txt');
|
|
2671
|
+
const judgePromptFile = path_1.default.join(promptDir, 'judge-prompt.txt');
|
|
2672
|
+
fs_1.default.writeFileSync(agentPromptFile, 'Agent prompt');
|
|
2673
|
+
fs_1.default.writeFileSync(judgePromptFile, 'Judge prompt');
|
|
2674
|
+
const configContent = {
|
|
2675
|
+
agents: [{
|
|
2676
|
+
...createTestAgentConfig(),
|
|
2677
|
+
systemPromptPath: agentPromptFile,
|
|
2678
|
+
}],
|
|
2679
|
+
judge: {
|
|
2680
|
+
id: 'test-judge',
|
|
2681
|
+
name: 'Test Judge',
|
|
2682
|
+
role: 'generalist',
|
|
2683
|
+
model: 'gpt-4',
|
|
2684
|
+
provider: 'openai',
|
|
2685
|
+
temperature: 0.3,
|
|
2686
|
+
systemPromptPath: judgePromptFile,
|
|
2687
|
+
},
|
|
2688
|
+
debate: createTestDebateConfig(),
|
|
2689
|
+
};
|
|
2690
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2691
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
2692
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--verbose', '--rounds', '1']);
|
|
2693
|
+
const calls = stderrWriteSpy.mock.calls.map(args => String(args[0]));
|
|
2694
|
+
const stderrContent = calls.join('');
|
|
2695
|
+
expect(stderrContent.length).toBeGreaterThan(0);
|
|
2696
|
+
stderrWriteSpy.mockRestore();
|
|
2697
|
+
});
|
|
2698
|
+
});
|
|
2699
|
+
describe('initializeTracingContext trace option branches', () => {
|
|
2700
|
+
it('should return undefined when trace is not LANGFUSE', async () => {
|
|
2701
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2702
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2703
|
+
trace: 'none',
|
|
2704
|
+
});
|
|
2705
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2706
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2707
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2708
|
+
});
|
|
2709
|
+
it('should handle tracing context creation failure gracefully', async () => {
|
|
2710
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2711
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2712
|
+
trace: 'langfuse',
|
|
2713
|
+
});
|
|
2714
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2715
|
+
dialecticCore.validateLangfuseConfig.mockImplementationOnce(() => {
|
|
2716
|
+
throw new Error('Langfuse config invalid');
|
|
2717
|
+
});
|
|
2718
|
+
const warnUserSpy = jest.spyOn(indexModule, 'warnUser').mockImplementation(() => { });
|
|
2719
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2720
|
+
expect(warnUserSpy).toHaveBeenCalledWith(expect.stringContaining('Langfuse tracing initialization failed'));
|
|
2721
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2722
|
+
dialecticCore.validateLangfuseConfig.mockReset();
|
|
2723
|
+
dialecticCore.validateLangfuseConfig.mockImplementation(jest.requireActual('dialectic-core').validateLangfuseConfig);
|
|
2724
|
+
warnUserSpy.mockRestore();
|
|
2725
|
+
});
|
|
2726
|
+
});
|
|
2727
|
+
describe('debateConfigFromSysConfig rounds branches', () => {
|
|
2728
|
+
it('should use sysConfig.debate.rounds when options.rounds is not provided', async () => {
|
|
2729
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2730
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2731
|
+
rounds: 5,
|
|
2732
|
+
});
|
|
2733
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2734
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath]);
|
|
2735
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2736
|
+
});
|
|
2737
|
+
});
|
|
2738
|
+
describe('agentConfigsFromSysConfig filtering branches', () => {
|
|
2739
|
+
it('should filter agents by role when --agents option is provided', async () => {
|
|
2740
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2741
|
+
const configContent = {
|
|
2742
|
+
agents: [
|
|
2743
|
+
createTestAgentConfig({ id: 'agent1', role: 'architect' }),
|
|
2744
|
+
createTestAgentConfig({ id: 'agent2', role: 'performance' }),
|
|
2745
|
+
createTestAgentConfig({ id: 'agent3', role: 'security' }),
|
|
2746
|
+
],
|
|
2747
|
+
judge: {
|
|
2748
|
+
id: 'test-judge',
|
|
2749
|
+
name: 'Test Judge',
|
|
2750
|
+
role: 'generalist',
|
|
2751
|
+
model: 'gpt-4',
|
|
2752
|
+
provider: 'openai',
|
|
2753
|
+
temperature: 0.3,
|
|
2754
|
+
},
|
|
2755
|
+
debate: createTestDebateConfig(),
|
|
2756
|
+
};
|
|
2757
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2758
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--agents', 'architect,performance', '--rounds', '1']);
|
|
2759
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2760
|
+
});
|
|
2761
|
+
it('should filter out disabled agents', async () => {
|
|
2762
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2763
|
+
const configContent = {
|
|
2764
|
+
agents: [
|
|
2765
|
+
createTestAgentConfig({ id: 'agent1', enabled: true }),
|
|
2766
|
+
createTestAgentConfig({ id: 'agent2', enabled: false }),
|
|
2767
|
+
createTestAgentConfig({ id: 'agent3', enabled: true }),
|
|
2768
|
+
],
|
|
2769
|
+
judge: {
|
|
2770
|
+
id: 'test-judge',
|
|
2771
|
+
name: 'Test Judge',
|
|
2772
|
+
role: 'generalist',
|
|
2773
|
+
model: 'gpt-4',
|
|
2774
|
+
provider: 'openai',
|
|
2775
|
+
temperature: 0.3,
|
|
2776
|
+
},
|
|
2777
|
+
debate: createTestDebateConfig(),
|
|
2778
|
+
};
|
|
2779
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2780
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2781
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2782
|
+
});
|
|
2783
|
+
});
|
|
2784
|
+
describe('outputVerboseDebateInfo path branches', () => {
|
|
2785
|
+
it('should handle undefined path when source is file', async () => {
|
|
2786
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2787
|
+
const promptDir = path_1.default.join(tmpDir, 'prompts');
|
|
2788
|
+
fs_1.default.mkdirSync(promptDir, { recursive: true });
|
|
2789
|
+
const agentPromptFile = path_1.default.join(promptDir, 'agent-prompt.txt');
|
|
2790
|
+
fs_1.default.writeFileSync(agentPromptFile, 'Agent prompt');
|
|
2791
|
+
const configContent = {
|
|
2792
|
+
agents: [{
|
|
2793
|
+
...createTestAgentConfig(),
|
|
2794
|
+
systemPromptPath: agentPromptFile,
|
|
2795
|
+
}],
|
|
2796
|
+
judge: {
|
|
2797
|
+
id: 'test-judge',
|
|
2798
|
+
name: 'Test Judge',
|
|
2799
|
+
role: 'generalist',
|
|
2800
|
+
model: 'gpt-4',
|
|
2801
|
+
provider: 'openai',
|
|
2802
|
+
temperature: 0.3,
|
|
2803
|
+
},
|
|
2804
|
+
debate: createTestDebateConfig(),
|
|
2805
|
+
};
|
|
2806
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2807
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
2808
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--verbose', '--rounds', '1']);
|
|
2809
|
+
expect(stderrWriteSpy).toHaveBeenCalled();
|
|
2810
|
+
stderrWriteSpy.mockRestore();
|
|
2811
|
+
});
|
|
2812
|
+
it('should handle agent not found in promptSources', async () => {
|
|
2813
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2814
|
+
const configContent = createTestConfigContent();
|
|
2815
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2816
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
2817
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--verbose', '--rounds', '1']);
|
|
2818
|
+
expect(stderrWriteSpy).toHaveBeenCalled();
|
|
2819
|
+
stderrWriteSpy.mockRestore();
|
|
2820
|
+
});
|
|
2821
|
+
});
|
|
2822
|
+
describe('outputRoundSummary metadata branches', () => {
|
|
2823
|
+
it('should handle contributions without metadata', async () => {
|
|
2824
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
2825
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--verbose', '--rounds', '1']);
|
|
2826
|
+
expect(stderrWriteSpy).toHaveBeenCalled();
|
|
2827
|
+
stderrWriteSpy.mockRestore();
|
|
2828
|
+
});
|
|
2829
|
+
it('should handle summaries with null tokensUsed and latencyMs', async () => {
|
|
2830
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
2831
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--verbose', '--rounds', '1']);
|
|
2832
|
+
expect(stderrWriteSpy).toHaveBeenCalled();
|
|
2833
|
+
stderrWriteSpy.mockRestore();
|
|
2834
|
+
});
|
|
2835
|
+
});
|
|
2836
|
+
describe('prompt resolution absPath branches', () => {
|
|
2837
|
+
it('should handle prompt resolution when absPath might be undefined', async () => {
|
|
2838
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2839
|
+
const promptDir = path_1.default.join(tmpDir, 'prompts');
|
|
2840
|
+
fs_1.default.mkdirSync(promptDir, { recursive: true });
|
|
2841
|
+
const agentPromptFile = path_1.default.join(promptDir, 'agent.txt');
|
|
2842
|
+
const judgePromptFile = path_1.default.join(promptDir, 'judge.txt');
|
|
2843
|
+
fs_1.default.writeFileSync(agentPromptFile, 'Agent prompt');
|
|
2844
|
+
fs_1.default.writeFileSync(judgePromptFile, 'Judge prompt');
|
|
2845
|
+
const configContent = {
|
|
2846
|
+
agents: [{
|
|
2847
|
+
...createTestAgentConfig(),
|
|
2848
|
+
systemPromptPath: 'prompts/agent.txt',
|
|
2849
|
+
}],
|
|
2850
|
+
judge: {
|
|
2851
|
+
id: 'test-judge',
|
|
2852
|
+
name: 'Test Judge',
|
|
2853
|
+
role: 'generalist',
|
|
2854
|
+
model: 'gpt-4',
|
|
2855
|
+
provider: 'openai',
|
|
2856
|
+
temperature: 0.3,
|
|
2857
|
+
systemPromptPath: 'prompts/judge.txt',
|
|
2858
|
+
},
|
|
2859
|
+
debate: createTestDebateConfig(),
|
|
2860
|
+
};
|
|
2861
|
+
const configParentDir = path_1.default.dirname(configPath);
|
|
2862
|
+
const promptsDir = path_1.default.join(configParentDir, 'prompts');
|
|
2863
|
+
fs_1.default.mkdirSync(promptsDir, { recursive: true });
|
|
2864
|
+
fs_1.default.writeFileSync(path_1.default.join(promptsDir, 'agent.txt'), 'Agent prompt');
|
|
2865
|
+
fs_1.default.writeFileSync(path_1.default.join(promptsDir, 'judge.txt'), 'Judge prompt');
|
|
2866
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2867
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2868
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2869
|
+
});
|
|
2870
|
+
});
|
|
2871
|
+
describe('createAgentLogger onlyVerbose undefined branch', () => {
|
|
2872
|
+
it('should handle onlyVerbose being undefined', async () => {
|
|
2873
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
2874
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
2875
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2876
|
+
stderrWriteSpy.mockRestore();
|
|
2877
|
+
});
|
|
2878
|
+
});
|
|
2879
|
+
describe('onSummarizationComplete verbose branch', () => {
|
|
2880
|
+
it('should log summarization details when verbose is true', async () => {
|
|
2881
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2882
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2883
|
+
summarization: {
|
|
2884
|
+
enabled: true,
|
|
2885
|
+
threshold: 100,
|
|
2886
|
+
maxLength: 500,
|
|
2887
|
+
method: 'length-based',
|
|
2888
|
+
},
|
|
2889
|
+
});
|
|
2890
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2891
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
2892
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--verbose', '--rounds', '1']);
|
|
2893
|
+
expect(stderrWriteSpy).toHaveBeenCalled();
|
|
2894
|
+
stderrWriteSpy.mockRestore();
|
|
2895
|
+
});
|
|
2896
|
+
});
|
|
2897
|
+
describe('initializeTracingContext optional metadata branches', () => {
|
|
2898
|
+
it('should handle undefined problemFileName and contextDirectoryName', async () => {
|
|
2899
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2900
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2901
|
+
trace: 'langfuse',
|
|
2902
|
+
});
|
|
2903
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2904
|
+
process.env.LANGFUSE_PUBLIC_KEY = 'test-key';
|
|
2905
|
+
process.env.LANGFUSE_SECRET_KEY = 'test-secret';
|
|
2906
|
+
process.env.LANGFUSE_HOST = 'https://cloud.langfuse.com';
|
|
2907
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2908
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2909
|
+
delete process.env.LANGFUSE_PUBLIC_KEY;
|
|
2910
|
+
delete process.env.LANGFUSE_SECRET_KEY;
|
|
2911
|
+
delete process.env.LANGFUSE_HOST;
|
|
2912
|
+
});
|
|
2913
|
+
it('should handle undefined judgeConfig in trace metadata', async () => {
|
|
2914
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2915
|
+
const configContent = {
|
|
2916
|
+
agents: [createTestAgentConfig()],
|
|
2917
|
+
debate: createTestDebateConfig({ trace: 'langfuse' }),
|
|
2918
|
+
};
|
|
2919
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2920
|
+
process.env.LANGFUSE_PUBLIC_KEY = 'test-key';
|
|
2921
|
+
process.env.LANGFUSE_SECRET_KEY = 'test-secret';
|
|
2922
|
+
process.env.LANGFUSE_HOST = 'https://cloud.langfuse.com';
|
|
2923
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2924
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2925
|
+
delete process.env.LANGFUSE_PUBLIC_KEY;
|
|
2926
|
+
delete process.env.LANGFUSE_SECRET_KEY;
|
|
2927
|
+
delete process.env.LANGFUSE_HOST;
|
|
2928
|
+
});
|
|
2929
|
+
});
|
|
2930
|
+
describe('createTracingContext falsy return branch', () => {
|
|
2931
|
+
it('should handle when createTracingContext returns undefined', async () => {
|
|
2932
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2933
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2934
|
+
trace: 'langfuse',
|
|
2935
|
+
});
|
|
2936
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2937
|
+
dialecticCore.createTracingContext.mockReturnValueOnce(undefined);
|
|
2938
|
+
process.env.LANGFUSE_PUBLIC_KEY = 'test-key';
|
|
2939
|
+
process.env.LANGFUSE_SECRET_KEY = 'test-secret';
|
|
2940
|
+
process.env.LANGFUSE_HOST = 'https://cloud.langfuse.com';
|
|
2941
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2942
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2943
|
+
dialecticCore.createTracingContext.mockReset();
|
|
2944
|
+
dialecticCore.createTracingContext.mockImplementation(jest.requireActual('dialectic-core').createTracingContext);
|
|
2945
|
+
delete process.env.LANGFUSE_PUBLIC_KEY;
|
|
2946
|
+
delete process.env.LANGFUSE_SECRET_KEY;
|
|
2947
|
+
delete process.env.LANGFUSE_HOST;
|
|
2948
|
+
});
|
|
2949
|
+
});
|
|
2950
|
+
describe('runDebateWithClarifications state machine error branch', () => {
|
|
2951
|
+
it('should throw for unknown suspend reason in state machine orchestrator', async () => {
|
|
2952
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2953
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2954
|
+
orchestratorType: 'state-machine',
|
|
2955
|
+
interactiveClarifications: true,
|
|
2956
|
+
});
|
|
2957
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2958
|
+
const coreModule = require('dialectic-core');
|
|
2959
|
+
const { EXECUTION_STATUS } = coreModule;
|
|
2960
|
+
const suspendResultWithUnknownReason = {
|
|
2961
|
+
status: EXECUTION_STATUS.SUSPENDED,
|
|
2962
|
+
suspendReason: 'UNKNOWN_REASON',
|
|
2963
|
+
suspendPayload: {
|
|
2964
|
+
debateId: 'debate-2',
|
|
2965
|
+
questions: [],
|
|
2966
|
+
},
|
|
2967
|
+
};
|
|
2968
|
+
const fakeOrchestrator = {
|
|
2969
|
+
runDebate: jest.fn().mockResolvedValue(suspendResultWithUnknownReason),
|
|
2970
|
+
resume: jest.fn(),
|
|
2971
|
+
};
|
|
2972
|
+
const createOrchestratorSpy = jest
|
|
2973
|
+
.spyOn(require('dialectic-core'), 'createOrchestrator')
|
|
2974
|
+
.mockReturnValue(fakeOrchestrator);
|
|
2975
|
+
const isStateMachineSpy = jest
|
|
2976
|
+
.spyOn(require('dialectic-core'), 'isStateMachineOrchestrator')
|
|
2977
|
+
.mockReturnValue(true);
|
|
2978
|
+
await expect((0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1'])).rejects.toThrow(/Unknown suspend reason/);
|
|
2979
|
+
createOrchestratorSpy.mockRestore();
|
|
2980
|
+
isStateMachineSpy.mockRestore();
|
|
2981
|
+
});
|
|
2982
|
+
});
|
|
2983
|
+
describe('outputRoundSummary summaries formatting branches', () => {
|
|
2984
|
+
it('should format summaries with tokens and latency metadata', async () => {
|
|
2985
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2986
|
+
const configContent = createTestConfigContent();
|
|
2987
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2988
|
+
const debateState = {
|
|
2989
|
+
id: 'debate-3',
|
|
2990
|
+
rounds: [
|
|
2991
|
+
{
|
|
2992
|
+
roundNumber: 1,
|
|
2993
|
+
summaries: {
|
|
2994
|
+
architect: {
|
|
2995
|
+
agentRole: 'architect',
|
|
2996
|
+
metadata: {
|
|
2997
|
+
beforeChars: 120,
|
|
2998
|
+
afterChars: 60,
|
|
2999
|
+
tokensUsed: 42,
|
|
3000
|
+
latencyMs: 150,
|
|
3001
|
+
method: 'length-based',
|
|
3002
|
+
},
|
|
3003
|
+
},
|
|
3004
|
+
},
|
|
3005
|
+
contributions: [],
|
|
3006
|
+
},
|
|
3007
|
+
],
|
|
3008
|
+
};
|
|
3009
|
+
const getDebateSpy = jest
|
|
3010
|
+
.spyOn(dialectic_core_1.StateManager.prototype, 'getDebate')
|
|
3011
|
+
.mockResolvedValueOnce(debateState);
|
|
3012
|
+
const stderrChunks = [];
|
|
3013
|
+
const stderrSpy = jest
|
|
3014
|
+
.spyOn(process.stderr, 'write')
|
|
3015
|
+
.mockImplementation((chunk) => {
|
|
3016
|
+
stderrChunks.push(String(chunk));
|
|
3017
|
+
return true;
|
|
3018
|
+
});
|
|
3019
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--verbose', '--rounds', '1']);
|
|
3020
|
+
const stderrOutput = stderrChunks.join('');
|
|
3021
|
+
expect(stderrOutput).toContain('summaries:');
|
|
3022
|
+
expect(stderrOutput).toContain('[architect]');
|
|
3023
|
+
expect(stderrOutput).toContain('latency=150ms, tokens=42, method=length-based');
|
|
3024
|
+
getDebateSpy.mockRestore();
|
|
3025
|
+
stderrSpy.mockRestore();
|
|
3026
|
+
});
|
|
3027
|
+
});
|
|
3028
|
+
describe('Targeted branch coverage tests', () => {
|
|
3029
|
+
it('should handle empty items in promptUserForAnswers', async () => {
|
|
3030
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
3031
|
+
const configContent = createTestConfigContent(undefined, {
|
|
3032
|
+
orchestratorType: 'state-machine',
|
|
3033
|
+
interactiveClarifications: true,
|
|
3034
|
+
});
|
|
3035
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
3036
|
+
mockedCollectClarifications.mockResolvedValueOnce([
|
|
3037
|
+
{ agentName: 'Test Agent', agentId: 'test-agent', role: 'architect', items: [] },
|
|
3038
|
+
]);
|
|
3039
|
+
mockReadlineWithAnswers([]);
|
|
3040
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
3041
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
3042
|
+
});
|
|
3043
|
+
it('should handle empty answer input (NA branch)', async () => {
|
|
3044
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
3045
|
+
const configContent = createTestConfigContent(undefined, { orchestratorType: 'classic' });
|
|
3046
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
3047
|
+
mockedCollectClarifications.mockResolvedValueOnce([
|
|
3048
|
+
{
|
|
3049
|
+
agentName: 'Test Agent',
|
|
3050
|
+
agentId: 'test-agent',
|
|
3051
|
+
role: 'architect',
|
|
3052
|
+
items: [{ id: 'q1', question: 'Question 1', answer: '' }],
|
|
3053
|
+
},
|
|
3054
|
+
]);
|
|
3055
|
+
mockReadlineWithAnswers(['']);
|
|
3056
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--clarify', '--rounds', '1']);
|
|
3057
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
3058
|
+
});
|
|
3059
|
+
it('should handle createAgentLogger with onlyVerbose explicitly false', async () => {
|
|
3060
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
3061
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
3062
|
+
});
|
|
3063
|
+
it('should handle onSummarizationComplete verbose branch', async () => {
|
|
3064
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
3065
|
+
const configContent = createTestConfigContent(undefined, {
|
|
3066
|
+
summarization: {
|
|
3067
|
+
enabled: true,
|
|
3068
|
+
threshold: 100,
|
|
3069
|
+
maxLength: 500,
|
|
3070
|
+
method: 'length-based',
|
|
3071
|
+
},
|
|
3072
|
+
});
|
|
3073
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
3074
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
3075
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--verbose', '--rounds', '1']);
|
|
3076
|
+
expect(stderrWriteSpy).toHaveBeenCalled();
|
|
3077
|
+
stderrWriteSpy.mockRestore();
|
|
3078
|
+
});
|
|
3079
|
+
it('should handle options.rounds branch in debateConfigFromSysConfig', async () => {
|
|
3080
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '2']);
|
|
3081
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
3082
|
+
});
|
|
3083
|
+
it('should handle rounds with summaries in outputRoundSummary', async () => {
|
|
3084
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
3085
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--verbose', '--rounds', '1']);
|
|
3086
|
+
expect(stderrWriteSpy).toHaveBeenCalled();
|
|
3087
|
+
stderrWriteSpy.mockRestore();
|
|
3088
|
+
});
|
|
3089
|
+
it('should handle contributions without metadata in outputRoundSummary', async () => {
|
|
3090
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
3091
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--verbose', '--rounds', '1']);
|
|
3092
|
+
expect(stderrWriteSpy).toHaveBeenCalled();
|
|
3093
|
+
stderrWriteSpy.mockRestore();
|
|
3094
|
+
});
|
|
3095
|
+
it('should handle classic orchestrator branch in outputVerboseDebateInfo', async () => {
|
|
3096
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
3097
|
+
const configContent = createTestConfigContent(undefined, {
|
|
3098
|
+
orchestratorType: 'classic',
|
|
3099
|
+
});
|
|
3100
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
3101
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
3102
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--verbose', '--rounds', '1']);
|
|
3103
|
+
const calls = stderrWriteSpy.mock.calls.map(args => String(args[0]));
|
|
3104
|
+
const stderrContent = calls.join('');
|
|
3105
|
+
expect(stderrContent).toContain('classic');
|
|
3106
|
+
stderrWriteSpy.mockRestore();
|
|
3107
|
+
});
|
|
3108
|
+
it('should handle clarificationRequested false branch', async () => {
|
|
3109
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
3110
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
3111
|
+
});
|
|
3112
|
+
it('should handle no context directory branch', async () => {
|
|
3113
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
3114
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
3115
|
+
});
|
|
3116
|
+
it('should handle path undefined in outputVerboseDebateInfo for agents', async () => {
|
|
3117
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
3118
|
+
const configContent = createTestConfigContent();
|
|
3119
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
3120
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
3121
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--verbose', '--rounds', '1']);
|
|
3122
|
+
expect(stderrWriteSpy).toHaveBeenCalled();
|
|
3123
|
+
stderrWriteSpy.mockRestore();
|
|
3124
|
+
});
|
|
3125
|
+
it('should handle path undefined in outputVerboseDebateInfo for judge', async () => {
|
|
3126
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
3127
|
+
const configContent = createTestConfigContent();
|
|
3128
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
3129
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
3130
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--verbose', '--rounds', '1']);
|
|
3131
|
+
expect(stderrWriteSpy).toHaveBeenCalled();
|
|
3132
|
+
stderrWriteSpy.mockRestore();
|
|
3133
|
+
});
|
|
3134
|
+
it('should handle totalTokens null branch in outputResults', async () => {
|
|
3135
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
3136
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--verbose', '--rounds', '1']);
|
|
3137
|
+
expect(stderrWriteSpy).toHaveBeenCalled();
|
|
3138
|
+
stderrWriteSpy.mockRestore();
|
|
3139
|
+
});
|
|
3140
|
+
it('should handle undefined configDir in resolveJudgeSystemPromptWithDefault', async () => {
|
|
3141
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
3142
|
+
const configContent = {
|
|
3143
|
+
agents: [createTestAgentConfig()],
|
|
3144
|
+
judge: {
|
|
3145
|
+
id: 'test-judge',
|
|
3146
|
+
name: 'Test Judge',
|
|
3147
|
+
role: 'generalist',
|
|
3148
|
+
model: 'gpt-4',
|
|
3149
|
+
provider: 'openai',
|
|
3150
|
+
temperature: 0.3,
|
|
3151
|
+
},
|
|
3152
|
+
debate: createTestDebateConfig(),
|
|
3153
|
+
};
|
|
3154
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
3155
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
3156
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
3157
|
+
});
|
|
3158
|
+
it('should handle undefined configDir in resolveJudgeSummaryPromptWithDefault', async () => {
|
|
3159
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
3160
|
+
const configContent = {
|
|
3161
|
+
agents: [createTestAgentConfig()],
|
|
3162
|
+
judge: {
|
|
3163
|
+
id: 'test-judge',
|
|
3164
|
+
name: 'Test Judge',
|
|
3165
|
+
role: 'generalist',
|
|
3166
|
+
model: 'gpt-4',
|
|
3167
|
+
provider: 'openai',
|
|
3168
|
+
temperature: 0.3,
|
|
3169
|
+
summaryPromptPath: 'nonexistent.txt',
|
|
3170
|
+
},
|
|
3171
|
+
debate: createTestDebateConfig(),
|
|
3172
|
+
};
|
|
3173
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
3174
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
3175
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
3176
|
+
});
|
|
1482
3177
|
});
|
|
1483
3178
|
});
|
|
1484
3179
|
});
|