karajan-code 1.36.1 → 1.37.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/README.md +31 -30
- package/docs/README.es.md +35 -34
- package/package.json +1 -1
- package/src/orchestrator/iteration-stages.js +50 -15
- package/src/orchestrator/post-loop-stages.js +25 -11
- package/src/orchestrator/pre-loop-stages.js +28 -20
- package/src/orchestrator/preflight-checks.js +3 -3
- package/src/orchestrator/solomon-escalation.js +3 -2
- package/src/orchestrator.js +6 -5
- package/src/utils/display.js +133 -23
- package/src/utils/injection-guard.js +171 -0
|
@@ -98,7 +98,8 @@ export async function runTesterStage({ config, logger, emitter, eventBase, sessi
|
|
|
98
98
|
emitProgress(
|
|
99
99
|
emitter,
|
|
100
100
|
makeEvent("tester:start", { ...eventBase, stage: "tester" }, {
|
|
101
|
-
message: "Tester evaluating test quality"
|
|
101
|
+
message: "Tester evaluating test quality",
|
|
102
|
+
detail: { provider: config?.roles?.tester?.provider || config?.roles?.coder?.provider || config?.coder || "claude", executorType: "agent" }
|
|
102
103
|
})
|
|
103
104
|
);
|
|
104
105
|
|
|
@@ -126,11 +127,13 @@ export async function runTesterStage({ config, logger, emitter, eventBase, sessi
|
|
|
126
127
|
attempts: attempts.length > 1 ? attempts : undefined
|
|
127
128
|
});
|
|
128
129
|
|
|
130
|
+
const testerProvider = provider || coderRole.provider;
|
|
129
131
|
emitProgress(
|
|
130
132
|
emitter,
|
|
131
133
|
makeEvent("tester:end", { ...eventBase, stage: "tester" }, {
|
|
132
134
|
status: testerOutput.ok ? "ok" : "fail",
|
|
133
|
-
message: testerOutput.ok ? "Tester passed" : `Tester: ${testerOutput.summary}
|
|
135
|
+
message: testerOutput.ok ? "Tester passed" : `Tester: ${testerOutput.summary}`,
|
|
136
|
+
detail: { ok: testerOutput.ok, summary: testerOutput.summary, provider: testerProvider, executorType: "agent" }
|
|
134
137
|
})
|
|
135
138
|
);
|
|
136
139
|
|
|
@@ -143,7 +146,7 @@ export async function runTesterStage({ config, logger, emitter, eventBase, sessi
|
|
|
143
146
|
makeEvent("tester:auto-continue", { ...eventBase, stage: "tester" }, {
|
|
144
147
|
status: "warn",
|
|
145
148
|
message: `Tester issues are advisory (reviewer approved), continuing: ${testerOutput.summary}`,
|
|
146
|
-
detail: { summary: testerOutput.summary, auto_continued: true }
|
|
149
|
+
detail: { summary: testerOutput.summary, auto_continued: true, provider: testerProvider, executorType: "agent" }
|
|
147
150
|
})
|
|
148
151
|
);
|
|
149
152
|
return { action: "ok", stageResult: { ok: false, summary: testerOutput.summary || "Tester issues (advisory)", auto_continued: true } };
|
|
@@ -158,7 +161,8 @@ export async function runSecurityStage({ config, logger, emitter, eventBase, ses
|
|
|
158
161
|
emitProgress(
|
|
159
162
|
emitter,
|
|
160
163
|
makeEvent("security:start", { ...eventBase, stage: "security" }, {
|
|
161
|
-
message: "Security auditing code"
|
|
164
|
+
message: "Security auditing code",
|
|
165
|
+
detail: { provider: config?.roles?.security?.provider || config?.roles?.coder?.provider || config?.coder || "claude", executorType: "agent" }
|
|
162
166
|
})
|
|
163
167
|
);
|
|
164
168
|
|
|
@@ -186,11 +190,13 @@ export async function runSecurityStage({ config, logger, emitter, eventBase, ses
|
|
|
186
190
|
attempts: attempts.length > 1 ? attempts : undefined
|
|
187
191
|
});
|
|
188
192
|
|
|
193
|
+
const securityProvider = provider || coderRole.provider;
|
|
189
194
|
emitProgress(
|
|
190
195
|
emitter,
|
|
191
196
|
makeEvent("security:end", { ...eventBase, stage: "security" }, {
|
|
192
197
|
status: securityOutput.ok ? "ok" : "fail",
|
|
193
|
-
message: securityOutput.ok ? "Security audit passed" : `Security: ${securityOutput.summary}
|
|
198
|
+
message: securityOutput.ok ? "Security audit passed" : `Security: ${securityOutput.summary}`,
|
|
199
|
+
detail: { ok: securityOutput.ok, summary: securityOutput.summary, provider: securityProvider, executorType: "agent" }
|
|
194
200
|
})
|
|
195
201
|
);
|
|
196
202
|
|
|
@@ -246,7 +252,8 @@ export async function runImpeccableStage({ config, logger, emitter, eventBase, s
|
|
|
246
252
|
emitProgress(
|
|
247
253
|
emitter,
|
|
248
254
|
makeEvent("impeccable:start", { ...eventBase, stage: "impeccable" }, {
|
|
249
|
-
message: "Impeccable auditing frontend design quality"
|
|
255
|
+
message: "Impeccable auditing frontend design quality",
|
|
256
|
+
detail: { provider: config?.roles?.impeccable?.provider || coderRole.provider, executorType: "agent" }
|
|
250
257
|
})
|
|
251
258
|
);
|
|
252
259
|
|
|
@@ -277,13 +284,15 @@ export async function runImpeccableStage({ config, logger, emitter, eventBase, s
|
|
|
277
284
|
});
|
|
278
285
|
|
|
279
286
|
const verdict = impeccableOutput.result?.verdict || "APPROVED";
|
|
287
|
+
const impeccableProvider = config?.roles?.impeccable?.provider || coderRole.provider;
|
|
280
288
|
emitProgress(
|
|
281
289
|
emitter,
|
|
282
290
|
makeEvent("impeccable:end", { ...eventBase, stage: "impeccable" }, {
|
|
283
291
|
status: impeccableOutput.ok ? "ok" : "fail",
|
|
284
292
|
message: impeccableOutput.ok
|
|
285
293
|
? (verdict === "IMPROVED" ? "Impeccable applied design fixes" : "Impeccable audit passed")
|
|
286
|
-
: `Impeccable: ${impeccableOutput.summary}
|
|
294
|
+
: `Impeccable: ${impeccableOutput.summary}`,
|
|
295
|
+
detail: { provider: impeccableProvider, executorType: "agent" }
|
|
287
296
|
})
|
|
288
297
|
);
|
|
289
298
|
|
|
@@ -296,7 +305,8 @@ export async function runFinalAuditStage({ config, logger, emitter, eventBase, s
|
|
|
296
305
|
emitProgress(
|
|
297
306
|
emitter,
|
|
298
307
|
makeEvent("audit:start", { ...eventBase, stage: "audit" }, {
|
|
299
|
-
message: "Final audit — verifying code quality"
|
|
308
|
+
message: "Final audit — verifying code quality",
|
|
309
|
+
detail: { provider: config?.roles?.audit?.provider || config?.roles?.coder?.provider || config?.coder || "claude", executorType: "agent" }
|
|
300
310
|
})
|
|
301
311
|
);
|
|
302
312
|
|
|
@@ -324,6 +334,7 @@ export async function runFinalAuditStage({ config, logger, emitter, eventBase, s
|
|
|
324
334
|
attempts: attempts.length > 1 ? attempts : undefined
|
|
325
335
|
});
|
|
326
336
|
|
|
337
|
+
const auditProvider = provider || coderRole.provider;
|
|
327
338
|
if (!auditOutput.ok) {
|
|
328
339
|
// Audit agent failed to run — treat as advisory, don't block pipeline
|
|
329
340
|
logger.warn(`Audit agent error (advisory): ${auditOutput.summary}`);
|
|
@@ -331,7 +342,8 @@ export async function runFinalAuditStage({ config, logger, emitter, eventBase, s
|
|
|
331
342
|
emitter,
|
|
332
343
|
makeEvent("audit:end", { ...eventBase, stage: "audit" }, {
|
|
333
344
|
status: "warn",
|
|
334
|
-
message: `Audit: agent error (advisory), continuing — ${auditOutput.summary}
|
|
345
|
+
message: `Audit: agent error (advisory), continuing — ${auditOutput.summary}`,
|
|
346
|
+
detail: { provider: auditProvider, executorType: "agent" }
|
|
335
347
|
})
|
|
336
348
|
);
|
|
337
349
|
return { action: "ok", stageResult: { ok: false, summary: auditOutput.summary || "Audit agent error (advisory)", auto_continued: true } };
|
|
@@ -374,7 +386,8 @@ export async function runFinalAuditStage({ config, logger, emitter, eventBase, s
|
|
|
374
386
|
emitter,
|
|
375
387
|
makeEvent("audit:end", { ...eventBase, stage: "audit" }, {
|
|
376
388
|
status: "fail",
|
|
377
|
-
message: `Audit: ${criticalCount + highCount} issue(s) found, sending back to coder
|
|
389
|
+
message: `Audit: ${criticalCount + highCount} issue(s) found, sending back to coder`,
|
|
390
|
+
detail: { provider: auditProvider, executorType: "agent" }
|
|
378
391
|
})
|
|
379
392
|
);
|
|
380
393
|
|
|
@@ -392,7 +405,8 @@ export async function runFinalAuditStage({ config, logger, emitter, eventBase, s
|
|
|
392
405
|
emitter,
|
|
393
406
|
makeEvent("audit:end", { ...eventBase, stage: "audit" }, {
|
|
394
407
|
status: "ok",
|
|
395
|
-
message: certifiedMsg
|
|
408
|
+
message: certifiedMsg,
|
|
409
|
+
detail: { provider: auditProvider, executorType: "agent" }
|
|
396
410
|
})
|
|
397
411
|
);
|
|
398
412
|
|
|
@@ -48,14 +48,16 @@ function applyModelSelection(triageOutput, config, emitter, eventBase) {
|
|
|
48
48
|
|
|
49
49
|
export async function runTriageStage({ config, logger, emitter, eventBase, session, coderRole, trackBudget }) {
|
|
50
50
|
logger.setContext({ iteration: 0, stage: "triage" });
|
|
51
|
+
|
|
52
|
+
const triageProvider = config?.roles?.triage?.provider || coderRole.provider;
|
|
51
53
|
emitProgress(
|
|
52
54
|
emitter,
|
|
53
55
|
makeEvent("triage:start", { ...eventBase, stage: "triage" }, {
|
|
54
|
-
message: "Triage classifying task complexity"
|
|
56
|
+
message: "Triage classifying task complexity",
|
|
57
|
+
detail: { provider: triageProvider, executorType: "agent" }
|
|
55
58
|
})
|
|
56
59
|
);
|
|
57
60
|
|
|
58
|
-
const triageProvider = config?.roles?.triage?.provider || coderRole.provider;
|
|
59
61
|
const triageOnOutput = ({ stream, line }) => {
|
|
60
62
|
emitProgress(emitter, makeEvent("agent:output", { ...eventBase, stage: "triage" }, {
|
|
61
63
|
message: line,
|
|
@@ -132,7 +134,7 @@ export async function runTriageStage({ config, logger, emitter, eventBase, sessi
|
|
|
132
134
|
makeEvent("triage:end", { ...eventBase, stage: "triage" }, {
|
|
133
135
|
status: triageOutput.ok ? "ok" : "fail",
|
|
134
136
|
message: triageOutput.ok ? "Triage completed" : `Triage failed: ${triageOutput.summary}`,
|
|
135
|
-
detail: stageResult
|
|
137
|
+
detail: { ...stageResult, provider: triageProvider, executorType: "agent" }
|
|
136
138
|
})
|
|
137
139
|
);
|
|
138
140
|
|
|
@@ -141,14 +143,16 @@ export async function runTriageStage({ config, logger, emitter, eventBase, sessi
|
|
|
141
143
|
|
|
142
144
|
export async function runResearcherStage({ config, logger, emitter, eventBase, session, coderRole, trackBudget }) {
|
|
143
145
|
logger.setContext({ iteration: 0, stage: "researcher" });
|
|
146
|
+
|
|
147
|
+
const researcherProvider = config?.roles?.researcher?.provider || coderRole.provider;
|
|
144
148
|
emitProgress(
|
|
145
149
|
emitter,
|
|
146
150
|
makeEvent("researcher:start", { ...eventBase, stage: "researcher" }, {
|
|
147
|
-
message: "Researcher investigating codebase"
|
|
151
|
+
message: "Researcher investigating codebase",
|
|
152
|
+
detail: { researcher: researcherProvider, provider: researcherProvider, executorType: "agent" }
|
|
148
153
|
})
|
|
149
154
|
);
|
|
150
155
|
|
|
151
|
-
const researcherProvider = config?.roles?.researcher?.provider || coderRole.provider;
|
|
152
156
|
const researcherOnOutput = ({ stream, line }) => {
|
|
153
157
|
emitProgress(emitter, makeEvent("agent:output", { ...eventBase, stage: "researcher" }, {
|
|
154
158
|
message: line,
|
|
@@ -191,7 +195,8 @@ export async function runResearcherStage({ config, logger, emitter, eventBase, s
|
|
|
191
195
|
emitter,
|
|
192
196
|
makeEvent("researcher:end", { ...eventBase, stage: "researcher" }, {
|
|
193
197
|
status: researchOutput.ok ? "ok" : "fail",
|
|
194
|
-
message: researchOutput.ok ? "Research completed" : `Research failed: ${researchOutput.summary}
|
|
198
|
+
message: researchOutput.ok ? "Research completed" : `Research failed: ${researchOutput.summary}`,
|
|
199
|
+
detail: { provider: researcherProvider, executorType: "agent" }
|
|
195
200
|
})
|
|
196
201
|
);
|
|
197
202
|
|
|
@@ -260,14 +265,14 @@ async function handleArchitectClarification({ architectOutput, askQuestion, conf
|
|
|
260
265
|
|
|
261
266
|
export async function runArchitectStage({ config, logger, emitter, eventBase, session, coderRole, trackBudget, researchContext = null, discoverResult = null, triageLevel = null, askQuestion = null }) {
|
|
262
267
|
logger.setContext({ iteration: 0, stage: "architect" });
|
|
268
|
+
const architectProvider = config?.roles?.architect?.provider || coderRole.provider;
|
|
263
269
|
emitProgress(
|
|
264
270
|
emitter,
|
|
265
271
|
makeEvent("architect:start", { ...eventBase, stage: "architect" }, {
|
|
266
|
-
message: "Architect designing solution architecture"
|
|
272
|
+
message: "Architect designing solution architecture",
|
|
273
|
+
detail: { architect: architectProvider, provider: architectProvider, executorType: "agent" }
|
|
267
274
|
})
|
|
268
275
|
);
|
|
269
|
-
|
|
270
|
-
const architectProvider = config?.roles?.architect?.provider || coderRole.provider;
|
|
271
276
|
const architectOnOutput = ({ stream, line }) => {
|
|
272
277
|
emitProgress(emitter, makeEvent("agent:output", { ...eventBase, stage: "architect" }, {
|
|
273
278
|
message: line,
|
|
@@ -330,7 +335,7 @@ export async function runArchitectStage({ config, logger, emitter, eventBase, se
|
|
|
330
335
|
makeEvent("architect:end", { ...eventBase, stage: "architect" }, {
|
|
331
336
|
status: architectOutput.ok ? "ok" : "fail",
|
|
332
337
|
message: architectOutput.ok ? "Architecture completed" : `Architecture failed: ${architectOutput.summary}`,
|
|
333
|
-
detail: stageResult
|
|
338
|
+
detail: { ...stageResult, provider: architectProvider, executorType: "agent" }
|
|
334
339
|
})
|
|
335
340
|
);
|
|
336
341
|
|
|
@@ -373,7 +378,7 @@ export async function runPlannerStage({ config, logger, emitter, eventBase, sess
|
|
|
373
378
|
emitter,
|
|
374
379
|
makeEvent("planner:start", { ...eventBase, stage: "planner" }, {
|
|
375
380
|
message: `Planner (${plannerRole.provider}) running`,
|
|
376
|
-
detail: { planner: plannerRole.provider }
|
|
381
|
+
detail: { planner: plannerRole.provider, provider: plannerRole.provider, executorType: "agent" }
|
|
377
382
|
})
|
|
378
383
|
);
|
|
379
384
|
|
|
@@ -416,7 +421,8 @@ export async function runPlannerStage({ config, logger, emitter, eventBase, sess
|
|
|
416
421
|
emitter,
|
|
417
422
|
makeEvent("planner:end", { ...eventBase, stage: "planner" }, {
|
|
418
423
|
status: "fail",
|
|
419
|
-
message: `Planner failed: ${details}
|
|
424
|
+
message: `Planner failed: ${details}`,
|
|
425
|
+
detail: { provider: plannerRole.provider, executorType: "agent" }
|
|
420
426
|
})
|
|
421
427
|
);
|
|
422
428
|
throw new Error(`Planner failed: ${details}`);
|
|
@@ -436,7 +442,8 @@ export async function runPlannerStage({ config, logger, emitter, eventBase, sess
|
|
|
436
442
|
emitProgress(
|
|
437
443
|
emitter,
|
|
438
444
|
makeEvent("planner:end", { ...eventBase, stage: "planner" }, {
|
|
439
|
-
message: "Planner completed"
|
|
445
|
+
message: "Planner completed",
|
|
446
|
+
detail: { provider: plannerRole.provider, executorType: "agent" }
|
|
440
447
|
})
|
|
441
448
|
);
|
|
442
449
|
|
|
@@ -445,14 +452,14 @@ export async function runPlannerStage({ config, logger, emitter, eventBase, sess
|
|
|
445
452
|
|
|
446
453
|
export async function runDiscoverStage({ config, logger, emitter, eventBase, session, coderRole, trackBudget }) {
|
|
447
454
|
logger.setContext({ iteration: 0, stage: "discover" });
|
|
455
|
+
const discoverProvider = config?.roles?.discover?.provider || coderRole.provider;
|
|
448
456
|
emitProgress(
|
|
449
457
|
emitter,
|
|
450
458
|
makeEvent("discover:start", { ...eventBase, stage: "discover" }, {
|
|
451
|
-
message: "Discover analyzing task for gaps"
|
|
459
|
+
message: "Discover analyzing task for gaps",
|
|
460
|
+
detail: { discover: discoverProvider, provider: discoverProvider, executorType: "agent" }
|
|
452
461
|
})
|
|
453
462
|
);
|
|
454
|
-
|
|
455
|
-
const discoverProvider = config?.roles?.discover?.provider || coderRole.provider;
|
|
456
463
|
const discoverOnOutput = ({ stream, line }) => {
|
|
457
464
|
emitProgress(emitter, makeEvent("agent:output", { ...eventBase, stage: "discover" }, {
|
|
458
465
|
message: line,
|
|
@@ -504,7 +511,7 @@ export async function runDiscoverStage({ config, logger, emitter, eventBase, ses
|
|
|
504
511
|
makeEvent("discover:end", { ...eventBase, stage: "discover" }, {
|
|
505
512
|
status: discoverOutput.ok ? "ok" : "fail",
|
|
506
513
|
message: discoverOutput.ok ? "Discovery completed" : `Discovery failed: ${discoverOutput.summary}`,
|
|
507
|
-
detail: stageResult
|
|
514
|
+
detail: { ...stageResult, provider: discoverProvider, executorType: "agent" }
|
|
508
515
|
})
|
|
509
516
|
);
|
|
510
517
|
|
|
@@ -518,10 +525,12 @@ export async function runDiscoverStage({ config, logger, emitter, eventBase, ses
|
|
|
518
525
|
*/
|
|
519
526
|
export async function runHuReviewerStage({ config, logger, emitter, eventBase, session, coderRole, trackBudget, huFile, askQuestion }) {
|
|
520
527
|
logger.setContext({ iteration: 0, stage: "hu-reviewer" });
|
|
528
|
+
const huReviewerProvider = config?.roles?.hu_reviewer?.provider || coderRole.provider;
|
|
521
529
|
emitProgress(
|
|
522
530
|
emitter,
|
|
523
531
|
makeEvent("hu-reviewer:start", { ...eventBase, stage: "hu-reviewer" }, {
|
|
524
|
-
message: "HU Reviewer certifying user stories"
|
|
532
|
+
message: "HU Reviewer certifying user stories",
|
|
533
|
+
detail: { provider: huReviewerProvider, executorType: "agent" }
|
|
525
534
|
})
|
|
526
535
|
);
|
|
527
536
|
|
|
@@ -569,7 +578,6 @@ export async function runHuReviewerStage({ config, logger, emitter, eventBase, s
|
|
|
569
578
|
}
|
|
570
579
|
|
|
571
580
|
// --- Evaluate loop (re-evaluate entire batch until all certified or needs_context with no askQuestion) ---
|
|
572
|
-
const huReviewerProvider = config?.roles?.hu_reviewer?.provider || coderRole.provider;
|
|
573
581
|
const huReviewerOnOutput = ({ stream, line }) => {
|
|
574
582
|
emitProgress(emitter, makeEvent("agent:output", { ...eventBase, stage: "hu-reviewer" }, {
|
|
575
583
|
message: line,
|
|
@@ -711,7 +719,7 @@ export async function runHuReviewerStage({ config, logger, emitter, eventBase, s
|
|
|
711
719
|
makeEvent("hu-reviewer:end", { ...eventBase, stage: "hu-reviewer" }, {
|
|
712
720
|
status: "ok",
|
|
713
721
|
message: `HU Review complete: ${certifiedStories.length}/${batch.stories.length} certified`,
|
|
714
|
-
detail: stageResult
|
|
722
|
+
detail: { ...stageResult, provider: huReviewerProvider, executorType: "agent" }
|
|
715
723
|
})
|
|
716
724
|
);
|
|
717
725
|
|
|
@@ -167,14 +167,14 @@ export async function runPreflightChecks({ config, logger, emitter, eventBase, r
|
|
|
167
167
|
logger.info("Preflight: skipped (no sonar, no security)");
|
|
168
168
|
emitProgress(emitter, makeEvent("preflight:end", { ...eventBase, stage: "preflight" }, {
|
|
169
169
|
message: "Preflight skipped (no checks needed)",
|
|
170
|
-
detail: result
|
|
170
|
+
detail: { ...result, executorType: "local" }
|
|
171
171
|
}));
|
|
172
172
|
return result;
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
emitProgress(emitter, makeEvent("preflight:start", { ...eventBase, stage: "preflight" }, {
|
|
176
176
|
message: "Running preflight environment checks",
|
|
177
|
-
detail: { sonarEnabled, securityEnabled }
|
|
177
|
+
detail: { sonarEnabled, securityEnabled, executorType: "local" }
|
|
178
178
|
}));
|
|
179
179
|
|
|
180
180
|
// --- 1. Docker (only if sonar enabled and not external) ---
|
|
@@ -286,7 +286,7 @@ export async function runPreflightChecks({ config, logger, emitter, eventBase, r
|
|
|
286
286
|
: hasWarnings
|
|
287
287
|
? `Preflight completed with ${result.warnings.length} warning(s)`
|
|
288
288
|
: "Preflight passed — all checks OK",
|
|
289
|
-
detail: result
|
|
289
|
+
detail: { ...result, executorType: "local" }
|
|
290
290
|
}));
|
|
291
291
|
|
|
292
292
|
return result;
|
|
@@ -9,11 +9,12 @@ export async function invokeSolomon({ config, logger, emitter, eventBase, stage,
|
|
|
9
9
|
return escalateToHuman({ askQuestion, session, emitter, eventBase, stage, conflict, iteration });
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
const solomonProvider = config?.roles?.solomon?.provider || "gemini";
|
|
12
13
|
emitProgress(
|
|
13
14
|
emitter,
|
|
14
15
|
makeEvent("solomon:start", { ...eventBase, stage: "solomon" }, {
|
|
15
16
|
message: `Solomon arbitrating ${stage} conflict`,
|
|
16
|
-
detail: { conflictStage: stage }
|
|
17
|
+
detail: { conflictStage: stage, provider: solomonProvider, executorType: "agent" }
|
|
17
18
|
})
|
|
18
19
|
);
|
|
19
20
|
|
|
@@ -41,7 +42,7 @@ export async function invokeSolomon({ config, logger, emitter, eventBase, stage,
|
|
|
41
42
|
message: ruling.ok
|
|
42
43
|
? `Solomon ruling: ${ruling.result?.ruling || "unknown"}`
|
|
43
44
|
: `Solomon failed: ${(solomonError || ruling.summary || "unknown error").slice(0, 200)}`,
|
|
44
|
-
detail: ruling.result
|
|
45
|
+
detail: { ...ruling.result, provider: solomonProvider, executorType: "agent" }
|
|
45
46
|
})
|
|
46
47
|
);
|
|
47
48
|
|
package/src/orchestrator.js
CHANGED
|
@@ -177,7 +177,8 @@ function createBudgetManager({ config, emitter, eventBase }) {
|
|
|
177
177
|
max_budget_usd: budgetLimit,
|
|
178
178
|
warn_threshold_pct: warnThresholdPct,
|
|
179
179
|
pct_used: Number(pctUsed.toFixed(2)),
|
|
180
|
-
remaining_usd: budgetTracker.remaining(budgetLimit)
|
|
180
|
+
remaining_usd: budgetTracker.remaining(budgetLimit),
|
|
181
|
+
executorType: "system"
|
|
181
182
|
}
|
|
182
183
|
})
|
|
183
184
|
);
|
|
@@ -379,7 +380,7 @@ async function handleCheckpoint({ checkpointDisabled, askQuestion, lastCheckpoin
|
|
|
379
380
|
emitter,
|
|
380
381
|
makeEvent("session:checkpoint", { ...eventBase, iteration: i, stage: "checkpoint" }, {
|
|
381
382
|
message: `Checkpoint: progress detected, continuing (${elapsedStr} min elapsed)`,
|
|
382
|
-
detail: { elapsed_minutes: Number(elapsedStr), iterations_done: i - 1, stages: stagesCompleted, auto_continued: true }
|
|
383
|
+
detail: { elapsed_minutes: Number(elapsedStr), iterations_done: i - 1, stages: stagesCompleted, auto_continued: true, executorType: "system" }
|
|
383
384
|
})
|
|
384
385
|
);
|
|
385
386
|
return { action: "continue_loop", checkpointDisabled, lastCheckpointAt: Date.now(), lastCheckpointSnapshot: newSnapshot };
|
|
@@ -956,7 +957,7 @@ async function runGuardStages({ config, logger, emitter, eventBase, session, ite
|
|
|
956
957
|
const warnings = outputResult.violations.filter(v => v.severity === "warning");
|
|
957
958
|
emitProgress(emitter, makeEvent("guard:output", { ...eventBase, stage: "guard" }, {
|
|
958
959
|
message: `Output guard: ${critical.length} critical, ${warnings.length} warnings`,
|
|
959
|
-
detail: { violations: outputResult.violations }
|
|
960
|
+
detail: { violations: outputResult.violations, executorType: "local" }
|
|
960
961
|
}));
|
|
961
962
|
logger.info(`Output guard: ${outputResult.violations.length} violation(s) found`);
|
|
962
963
|
for (const v of outputResult.violations) {
|
|
@@ -983,7 +984,7 @@ async function runGuardStages({ config, logger, emitter, eventBase, session, ite
|
|
|
983
984
|
if (!perfResult.skipped && perfResult.violations.length > 0) {
|
|
984
985
|
emitProgress(emitter, makeEvent("guard:perf", { ...eventBase, stage: "guard" }, {
|
|
985
986
|
message: `Perf guard: ${perfResult.violations.length} issue(s)`,
|
|
986
|
-
detail: { violations: perfResult.violations }
|
|
987
|
+
detail: { violations: perfResult.violations, executorType: "local" }
|
|
987
988
|
}));
|
|
988
989
|
logger.info(`Perf guard: ${perfResult.violations.length} issue(s) found`);
|
|
989
990
|
for (const v of perfResult.violations) {
|
|
@@ -1165,7 +1166,7 @@ async function initFlowContext({ task, config, logger, emitter, askQuestion, pgT
|
|
|
1165
1166
|
logger.info(`RTK detected (${rtkResult.version}) — instructing agents to prefix Bash commands with rtk`);
|
|
1166
1167
|
emitProgress(emitter, makeEvent("rtk:detected", ctx.eventBase, {
|
|
1167
1168
|
message: "RTK detected — agent commands will use token optimization",
|
|
1168
|
-
detail: { version: rtkResult.version }
|
|
1169
|
+
detail: { version: rtkResult.version, executorType: "local" }
|
|
1169
1170
|
}));
|
|
1170
1171
|
}
|
|
1171
1172
|
|
package/src/utils/display.js
CHANGED
|
@@ -47,6 +47,20 @@ const ICONS = {
|
|
|
47
47
|
"budget:update": "\ud83d\udcb8",
|
|
48
48
|
"iteration:end": "\u23f1\ufe0f",
|
|
49
49
|
"session:end": "\ud83c\udfc1",
|
|
50
|
+
"discover:start": "\ud83d\udd0e",
|
|
51
|
+
"discover:end": "\ud83d\udd0e",
|
|
52
|
+
"architect:start": "\ud83c\udfdb\ufe0f",
|
|
53
|
+
"architect:end": "\ud83c\udfdb\ufe0f",
|
|
54
|
+
"hu-reviewer:start": "\ud83d\udcdd",
|
|
55
|
+
"hu-reviewer:end": "\ud83d\udcdd",
|
|
56
|
+
"impeccable:start": "\ud83c\udfa8",
|
|
57
|
+
"impeccable:end": "\ud83c\udfa8",
|
|
58
|
+
"audit:start": "\ud83d\udccb",
|
|
59
|
+
"audit:end": "\ud83d\udccb",
|
|
60
|
+
"sonarcloud:start": "\u2601\ufe0f",
|
|
61
|
+
"sonarcloud:end": "\u2601\ufe0f",
|
|
62
|
+
"preflight:start": "\ud83d\udee1\ufe0f",
|
|
63
|
+
"preflight:end": "\ud83d\udee1\ufe0f",
|
|
50
64
|
question: "\u2753"
|
|
51
65
|
};
|
|
52
66
|
|
|
@@ -95,24 +109,37 @@ export function printHeader({ task, config }) {
|
|
|
95
109
|
console.log();
|
|
96
110
|
}
|
|
97
111
|
|
|
112
|
+
/* ── Helper: executor/provider tag ───────────────────────────── */
|
|
113
|
+
|
|
114
|
+
function formatExecutor(detail) {
|
|
115
|
+
if (!detail) return '';
|
|
116
|
+
const provider = detail.provider || '';
|
|
117
|
+
const type = detail.executorType || '';
|
|
118
|
+
if (provider) return ` ${ANSI.dim}${provider}${ANSI.reset}`;
|
|
119
|
+
if (type === 'local') return ` ${ANSI.dim}local${ANSI.reset}`;
|
|
120
|
+
if (type === 'system') return ` ${ANSI.dim}system${ANSI.reset}`;
|
|
121
|
+
return '';
|
|
122
|
+
}
|
|
123
|
+
|
|
98
124
|
/* ── Helper: role start/end one-liners ───────────────────────── */
|
|
99
125
|
|
|
100
126
|
function roleStart(icon, label, provider) {
|
|
101
127
|
console.log(` \u251c\u2500 ${icon} ${label} (${provider || "?"}) running...`);
|
|
102
128
|
}
|
|
103
129
|
|
|
104
|
-
function roleEnd(status, label, elapsed) {
|
|
105
|
-
console.log(` \u251c\u2500 ${status} ${label} completed ${elapsed}`);
|
|
130
|
+
function roleEnd(status, label, elapsed, executor) {
|
|
131
|
+
console.log(` \u251c\u2500 ${status} ${label} completed${executor || ''} ${elapsed}`);
|
|
106
132
|
}
|
|
107
133
|
|
|
108
134
|
/* ── Helper: pass/fail stage result ─────────────────────────── */
|
|
109
135
|
|
|
110
136
|
function passFailStage(detail, label, failDefault, elapsed) {
|
|
137
|
+
const executor = formatExecutor(detail);
|
|
111
138
|
if (detail?.ok === false) {
|
|
112
139
|
const summary = detail?.summary || failDefault;
|
|
113
|
-
console.log(` \u251c\u2500 ${ANSI.red}\u274c ${label}: ${summary}${ANSI.reset} ${elapsed}`);
|
|
140
|
+
console.log(` \u251c\u2500 ${ANSI.red}\u274c ${label}: ${summary}${ANSI.reset}${executor} ${elapsed}`);
|
|
114
141
|
} else {
|
|
115
|
-
console.log(` \u251c\u2500 ${ANSI.green}\u2705 ${label}: passed${ANSI.reset} ${elapsed}`);
|
|
142
|
+
console.log(` \u251c\u2500 ${ANSI.green}\u2705 ${label}: passed${ANSI.reset}${executor} ${elapsed}`);
|
|
116
143
|
}
|
|
117
144
|
}
|
|
118
145
|
|
|
@@ -270,49 +297,52 @@ const EVENT_HANDLERS = {
|
|
|
270
297
|
roleStart(icon, "Planner", event.detail?.planner);
|
|
271
298
|
},
|
|
272
299
|
|
|
273
|
-
"planner:end": (
|
|
274
|
-
roleEnd(status, "Planner", elapsed);
|
|
300
|
+
"planner:end": (event, _icon, elapsed, status) => {
|
|
301
|
+
roleEnd(status, "Planner", elapsed, formatExecutor(event.detail));
|
|
275
302
|
},
|
|
276
303
|
|
|
277
304
|
"coder:start": (event, icon) => {
|
|
278
305
|
roleStart(icon, "Coder", event.detail?.coder);
|
|
279
306
|
},
|
|
280
307
|
|
|
281
|
-
"coder:end": (
|
|
282
|
-
roleEnd(status, "Coder", elapsed);
|
|
308
|
+
"coder:end": (event, _icon, elapsed, status) => {
|
|
309
|
+
roleEnd(status, "Coder", elapsed, formatExecutor(event.detail));
|
|
283
310
|
},
|
|
284
311
|
|
|
285
312
|
"refactorer:start": (event, icon) => {
|
|
286
313
|
roleStart(icon, "Refactorer", event.detail?.refactorer);
|
|
287
314
|
},
|
|
288
315
|
|
|
289
|
-
"refactorer:end": (
|
|
290
|
-
roleEnd(status, "Refactorer", elapsed);
|
|
316
|
+
"refactorer:end": (event, _icon, elapsed, status) => {
|
|
317
|
+
roleEnd(status, "Refactorer", elapsed, formatExecutor(event.detail));
|
|
291
318
|
},
|
|
292
319
|
|
|
293
320
|
"tdd:result": (event, icon) => {
|
|
294
321
|
const tdd = event.detail || {};
|
|
295
322
|
const label = tdd.ok ? `${ANSI.green}PASS${ANSI.reset}` : `${ANSI.red}FAIL${ANSI.reset}`;
|
|
296
323
|
const files = tdd.sourceFiles === undefined ? "" : ` (${tdd.sourceFiles} src, ${tdd.testFiles} test)`;
|
|
297
|
-
|
|
324
|
+
const executor = formatExecutor(event.detail);
|
|
325
|
+
console.log(` \u251c\u2500 ${icon} TDD policy: ${label}${files}${executor}`);
|
|
298
326
|
},
|
|
299
327
|
|
|
300
328
|
"researcher:start": (event, icon) => {
|
|
301
329
|
console.log(` \u251c\u2500 ${icon} Researcher (${event.detail?.researcher || "?"}) investigating...`);
|
|
302
330
|
},
|
|
303
331
|
|
|
304
|
-
"researcher:end": (
|
|
305
|
-
roleEnd(status, "Researcher", elapsed);
|
|
332
|
+
"researcher:end": (event, _icon, elapsed, status) => {
|
|
333
|
+
roleEnd(status, "Researcher", elapsed, formatExecutor(event.detail));
|
|
306
334
|
},
|
|
307
335
|
|
|
308
|
-
"sonar:start": (
|
|
309
|
-
|
|
336
|
+
"sonar:start": (event, icon) => {
|
|
337
|
+
const executor = formatExecutor(event.detail);
|
|
338
|
+
console.log(` \u251c\u2500 ${icon} SonarQube scanning...${executor}`);
|
|
310
339
|
},
|
|
311
340
|
|
|
312
341
|
"sonar:end": (event, _icon, elapsed, status) => {
|
|
313
342
|
const gate = event.detail?.gateStatus || "?";
|
|
314
343
|
const gateColor = gate === "OK" ? ANSI.green : ANSI.red;
|
|
315
|
-
|
|
344
|
+
const executor = formatExecutor(event.detail);
|
|
345
|
+
console.log(` \u251c\u2500 ${status} Quality gate: ${gateColor}${gate}${ANSI.reset}${executor} ${elapsed}`);
|
|
316
346
|
},
|
|
317
347
|
|
|
318
348
|
"reviewer:start": (event, icon) => {
|
|
@@ -321,11 +351,12 @@ const EVENT_HANDLERS = {
|
|
|
321
351
|
|
|
322
352
|
"reviewer:end": (event, _icon, elapsed) => {
|
|
323
353
|
const review = event.detail || {};
|
|
354
|
+
const executor = formatExecutor(event.detail);
|
|
324
355
|
if (review.approved) {
|
|
325
|
-
console.log(` \u251c\u2500 ${ANSI.green}\u2705 Review: APPROVED${ANSI.reset} ${elapsed}`);
|
|
356
|
+
console.log(` \u251c\u2500 ${ANSI.green}\u2705 Review: APPROVED${ANSI.reset}${executor} ${elapsed}`);
|
|
326
357
|
} else {
|
|
327
358
|
const count = review.blockingCount || 0;
|
|
328
|
-
console.log(` \u251c\u2500 ${ANSI.red}\u274c Review: REJECTED (${count} blocking)${ANSI.reset}`);
|
|
359
|
+
console.log(` \u251c\u2500 ${ANSI.red}\u274c Review: REJECTED (${count} blocking)${ANSI.reset}${executor}`);
|
|
329
360
|
if (review.issues) {
|
|
330
361
|
for (const issue of review.issues) {
|
|
331
362
|
console.log(` \u2502 ${ANSI.dim}${issue}${ANSI.reset}`);
|
|
@@ -334,16 +365,18 @@ const EVENT_HANDLERS = {
|
|
|
334
365
|
}
|
|
335
366
|
},
|
|
336
367
|
|
|
337
|
-
"tester:start": (
|
|
338
|
-
|
|
368
|
+
"tester:start": (event, icon) => {
|
|
369
|
+
const executor = formatExecutor(event.detail);
|
|
370
|
+
console.log(` \u251c\u2500 ${icon} Tester evaluating...${executor}`);
|
|
339
371
|
},
|
|
340
372
|
|
|
341
373
|
"tester:end": (event, _icon, elapsed) => {
|
|
342
374
|
passFailStage(event.detail, "Tester", "issues found", elapsed);
|
|
343
375
|
},
|
|
344
376
|
|
|
345
|
-
"security:start": (
|
|
346
|
-
|
|
377
|
+
"security:start": (event, icon) => {
|
|
378
|
+
const executor = formatExecutor(event.detail);
|
|
379
|
+
console.log(` \u251c\u2500 ${icon} Security auditing...${executor}`);
|
|
347
380
|
},
|
|
348
381
|
|
|
349
382
|
"security:end": (event, _icon, elapsed) => {
|
|
@@ -351,7 +384,8 @@ const EVENT_HANDLERS = {
|
|
|
351
384
|
},
|
|
352
385
|
|
|
353
386
|
"solomon:start": (event, icon) => {
|
|
354
|
-
|
|
387
|
+
const executor = formatExecutor(event.detail);
|
|
388
|
+
console.log(` \u251c\u2500 ${icon} Solomon arbitrating ${event.detail?.conflictStage || "?"} conflict...${executor}`);
|
|
355
389
|
},
|
|
356
390
|
|
|
357
391
|
"solomon:end": (event, _icon, elapsed) => {
|
|
@@ -422,6 +456,82 @@ const EVENT_HANDLERS = {
|
|
|
422
456
|
console.log(`${ANSI.dim}Resume with: kj resume ${event.sessionId} --answer "<response>"${ANSI.reset}`);
|
|
423
457
|
},
|
|
424
458
|
|
|
459
|
+
"preflight:start": (event) => {
|
|
460
|
+
const executor = formatExecutor(event.detail);
|
|
461
|
+
console.log(` \u251c\u2500 \ud83d\udee1\ufe0f Preflight checks running...${executor}`);
|
|
462
|
+
},
|
|
463
|
+
|
|
464
|
+
"preflight:end": (event, _icon, elapsed) => {
|
|
465
|
+
const executor = formatExecutor(event.detail);
|
|
466
|
+
const ok = event.status === "ok";
|
|
467
|
+
const statusIcon = ok ? `${ANSI.green}\u2705` : `${ANSI.red}\u274c`;
|
|
468
|
+
console.log(` \u251c\u2500 ${statusIcon} Preflight: ${event.message}${ANSI.reset}${executor} ${elapsed}`);
|
|
469
|
+
},
|
|
470
|
+
|
|
471
|
+
"discover:start": (event, icon) => {
|
|
472
|
+
const provider = event.detail?.provider || event.detail?.discover || "?";
|
|
473
|
+
console.log(` \u251c\u2500 ${icon || "\ud83d\udd0e"} Discover (${provider}) analyzing...`);
|
|
474
|
+
},
|
|
475
|
+
|
|
476
|
+
"discover:end": (event, _icon, elapsed, status) => {
|
|
477
|
+
roleEnd(status, "Discover", elapsed, formatExecutor(event.detail));
|
|
478
|
+
},
|
|
479
|
+
|
|
480
|
+
"architect:start": (event) => {
|
|
481
|
+
const provider = event.detail?.provider || event.detail?.architect || "?";
|
|
482
|
+
console.log(` \u251c\u2500 \ud83c\udfdb\ufe0f Architect (${provider}) designing...`);
|
|
483
|
+
},
|
|
484
|
+
|
|
485
|
+
"architect:end": (event, _icon, elapsed, status) => {
|
|
486
|
+
roleEnd(status, "Architect", elapsed, formatExecutor(event.detail));
|
|
487
|
+
},
|
|
488
|
+
|
|
489
|
+
"hu-reviewer:start": (event) => {
|
|
490
|
+
const executor = formatExecutor(event.detail);
|
|
491
|
+
console.log(` \u251c\u2500 \ud83d\udcdd HU Reviewer certifying...${executor}`);
|
|
492
|
+
},
|
|
493
|
+
|
|
494
|
+
"hu-reviewer:end": (event, _icon, elapsed, status) => {
|
|
495
|
+
roleEnd(status, "HU Reviewer", elapsed, formatExecutor(event.detail));
|
|
496
|
+
},
|
|
497
|
+
|
|
498
|
+
"impeccable:start": (event) => {
|
|
499
|
+
const executor = formatExecutor(event.detail);
|
|
500
|
+
console.log(` \u251c\u2500 \ud83c\udfa8 Impeccable auditing design...${executor}`);
|
|
501
|
+
},
|
|
502
|
+
|
|
503
|
+
"impeccable:end": (event, _icon, elapsed, status) => {
|
|
504
|
+
roleEnd(status, "Impeccable", elapsed, formatExecutor(event.detail));
|
|
505
|
+
},
|
|
506
|
+
|
|
507
|
+
"audit:start": (event) => {
|
|
508
|
+
const executor = formatExecutor(event.detail);
|
|
509
|
+
console.log(` \u251c\u2500 \ud83d\udccb Final audit running...${executor}`);
|
|
510
|
+
},
|
|
511
|
+
|
|
512
|
+
"audit:end": (event, _icon, elapsed, status) => {
|
|
513
|
+
roleEnd(status, "Audit", elapsed, formatExecutor(event.detail));
|
|
514
|
+
},
|
|
515
|
+
|
|
516
|
+
"sonarcloud:start": (event) => {
|
|
517
|
+
const executor = formatExecutor(event.detail);
|
|
518
|
+
console.log(` \u251c\u2500 \u2601\ufe0f SonarCloud scanning...${executor}`);
|
|
519
|
+
},
|
|
520
|
+
|
|
521
|
+
"sonarcloud:end": (event, _icon, elapsed, status) => {
|
|
522
|
+
roleEnd(status, "SonarCloud", elapsed, formatExecutor(event.detail));
|
|
523
|
+
},
|
|
524
|
+
|
|
525
|
+
"guard:output": (event) => {
|
|
526
|
+
const executor = formatExecutor(event.detail);
|
|
527
|
+
console.log(` \u251c\u2500 \ud83d\udee1\ufe0f ${event.message}${executor}`);
|
|
528
|
+
},
|
|
529
|
+
|
|
530
|
+
"guard:perf": (event) => {
|
|
531
|
+
const executor = formatExecutor(event.detail);
|
|
532
|
+
console.log(` \u251c\u2500 \u26a1 ${event.message}${executor}`);
|
|
533
|
+
},
|
|
534
|
+
|
|
425
535
|
"pipeline:tracker": (event) => {
|
|
426
536
|
const trackerStages = event.detail?.stages || [];
|
|
427
537
|
console.log(` ${ANSI.dim}\u250c Pipeline${ANSI.reset}`);
|