karajan-code 1.2.2 → 1.3.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 +2 -2
- package/docs/README.es.md +2 -2
- package/package.json +1 -1
- package/src/agents/availability.js +3 -9
- package/src/agents/index.js +32 -11
- package/src/agents/model-registry.js +62 -0
- package/src/config.js +9 -1
- package/src/mcp/orphan-guard.js +21 -0
- package/src/mcp/server.js +4 -0
- package/src/orchestrator/iteration-stages.js +404 -0
- package/src/orchestrator/post-loop-stages.js +141 -0
- package/src/orchestrator/pre-loop-stages.js +149 -0
- package/src/orchestrator/reviewer-fallback.js +39 -0
- package/src/orchestrator/solomon-escalation.js +84 -0
- package/src/orchestrator.js +80 -883
- package/src/planning-game/client.js +27 -17
- package/src/plugins/loader.js +67 -0
- package/src/prompts/planner.js +51 -0
- package/src/repeat-detector.js +11 -0
- package/src/roles/coder-role.js +4 -1
- package/src/roles/planner-role.js +2 -2
- package/src/roles/refactorer-role.js +2 -0
- package/src/roles/reviewer-role.js +13 -6
- package/src/session-cleanup.js +63 -0
- package/src/sonar/api.js +16 -3
- package/src/utils/budget.js +30 -0
- package/src/utils/pricing.js +3 -13
- package/src/utils/retry.js +88 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { TesterRole } from "../roles/tester-role.js";
|
|
2
|
+
import { SecurityRole } from "../roles/security-role.js";
|
|
3
|
+
import { addCheckpoint, saveSession } from "../session-store.js";
|
|
4
|
+
import { emitProgress, makeEvent } from "../utils/events.js";
|
|
5
|
+
import { invokeSolomon } from "./solomon-escalation.js";
|
|
6
|
+
|
|
7
|
+
export async function runTesterStage({ config, logger, emitter, eventBase, session, coderRole, trackBudget, iteration, task, diff, askQuestion }) {
|
|
8
|
+
logger.setContext({ iteration, stage: "tester" });
|
|
9
|
+
emitProgress(
|
|
10
|
+
emitter,
|
|
11
|
+
makeEvent("tester:start", { ...eventBase, stage: "tester" }, {
|
|
12
|
+
message: "Tester evaluating test quality"
|
|
13
|
+
})
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const tester = new TesterRole({ config, logger, emitter });
|
|
17
|
+
await tester.init({ task, iteration });
|
|
18
|
+
const testerStart = Date.now();
|
|
19
|
+
const testerOutput = await tester.run({ task, diff });
|
|
20
|
+
trackBudget({
|
|
21
|
+
role: "tester",
|
|
22
|
+
provider: config?.roles?.tester?.provider || coderRole.provider,
|
|
23
|
+
model: config?.roles?.tester?.model || coderRole.model,
|
|
24
|
+
result: testerOutput,
|
|
25
|
+
duration_ms: Date.now() - testerStart
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
await addCheckpoint(session, { stage: "tester", iteration, ok: testerOutput.ok });
|
|
29
|
+
|
|
30
|
+
emitProgress(
|
|
31
|
+
emitter,
|
|
32
|
+
makeEvent("tester:end", { ...eventBase, stage: "tester" }, {
|
|
33
|
+
status: testerOutput.ok ? "ok" : "fail",
|
|
34
|
+
message: testerOutput.ok ? "Tester passed" : `Tester: ${testerOutput.summary}`
|
|
35
|
+
})
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
if (!testerOutput.ok) {
|
|
39
|
+
const maxTesterRetries = config.session?.max_tester_retries ?? 1;
|
|
40
|
+
session.tester_retry_count = (session.tester_retry_count || 0) + 1;
|
|
41
|
+
await saveSession(session);
|
|
42
|
+
|
|
43
|
+
if (session.tester_retry_count >= maxTesterRetries) {
|
|
44
|
+
const solomonResult = await invokeSolomon({
|
|
45
|
+
config, logger, emitter, eventBase, stage: "tester", askQuestion, session, iteration,
|
|
46
|
+
conflict: {
|
|
47
|
+
stage: "tester",
|
|
48
|
+
task,
|
|
49
|
+
diff,
|
|
50
|
+
iterationCount: session.tester_retry_count,
|
|
51
|
+
maxIterations: maxTesterRetries,
|
|
52
|
+
history: [{ agent: "tester", feedback: testerOutput.summary }]
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (solomonResult.action === "pause") {
|
|
57
|
+
return { action: "pause", result: { paused: true, sessionId: session.id, question: solomonResult.question, context: "tester_fail_fast" } };
|
|
58
|
+
}
|
|
59
|
+
if (solomonResult.action === "subtask") {
|
|
60
|
+
return { action: "pause", result: { paused: true, sessionId: session.id, subtask: solomonResult.subtask, context: "tester_subtask" } };
|
|
61
|
+
}
|
|
62
|
+
// Solomon approved — proceed to next stage
|
|
63
|
+
return { action: "ok" };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
session.last_reviewer_feedback = `Tester feedback: ${testerOutput.summary}`;
|
|
67
|
+
await saveSession(session);
|
|
68
|
+
return { action: "continue" };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
session.tester_retry_count = 0;
|
|
72
|
+
return { action: "ok", stageResult: { ok: true, summary: testerOutput.summary || "All tests passed" } };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function runSecurityStage({ config, logger, emitter, eventBase, session, coderRole, trackBudget, iteration, task, diff, askQuestion }) {
|
|
76
|
+
logger.setContext({ iteration, stage: "security" });
|
|
77
|
+
emitProgress(
|
|
78
|
+
emitter,
|
|
79
|
+
makeEvent("security:start", { ...eventBase, stage: "security" }, {
|
|
80
|
+
message: "Security auditing code"
|
|
81
|
+
})
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const security = new SecurityRole({ config, logger, emitter });
|
|
85
|
+
await security.init({ task, iteration });
|
|
86
|
+
const securityStart = Date.now();
|
|
87
|
+
const securityOutput = await security.run({ task, diff });
|
|
88
|
+
trackBudget({
|
|
89
|
+
role: "security",
|
|
90
|
+
provider: config?.roles?.security?.provider || coderRole.provider,
|
|
91
|
+
model: config?.roles?.security?.model || coderRole.model,
|
|
92
|
+
result: securityOutput,
|
|
93
|
+
duration_ms: Date.now() - securityStart
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
await addCheckpoint(session, { stage: "security", iteration, ok: securityOutput.ok });
|
|
97
|
+
|
|
98
|
+
emitProgress(
|
|
99
|
+
emitter,
|
|
100
|
+
makeEvent("security:end", { ...eventBase, stage: "security" }, {
|
|
101
|
+
status: securityOutput.ok ? "ok" : "fail",
|
|
102
|
+
message: securityOutput.ok ? "Security audit passed" : `Security: ${securityOutput.summary}`
|
|
103
|
+
})
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
if (!securityOutput.ok) {
|
|
107
|
+
const maxSecurityRetries = config.session?.max_security_retries ?? 1;
|
|
108
|
+
session.security_retry_count = (session.security_retry_count || 0) + 1;
|
|
109
|
+
await saveSession(session);
|
|
110
|
+
|
|
111
|
+
if (session.security_retry_count >= maxSecurityRetries) {
|
|
112
|
+
const solomonResult = await invokeSolomon({
|
|
113
|
+
config, logger, emitter, eventBase, stage: "security", askQuestion, session, iteration,
|
|
114
|
+
conflict: {
|
|
115
|
+
stage: "security",
|
|
116
|
+
task,
|
|
117
|
+
diff,
|
|
118
|
+
iterationCount: session.security_retry_count,
|
|
119
|
+
maxIterations: maxSecurityRetries,
|
|
120
|
+
history: [{ agent: "security", feedback: securityOutput.summary }]
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
if (solomonResult.action === "pause") {
|
|
125
|
+
return { action: "pause", result: { paused: true, sessionId: session.id, question: solomonResult.question, context: "security_fail_fast" } };
|
|
126
|
+
}
|
|
127
|
+
if (solomonResult.action === "subtask") {
|
|
128
|
+
return { action: "pause", result: { paused: true, sessionId: session.id, subtask: solomonResult.subtask, context: "security_subtask" } };
|
|
129
|
+
}
|
|
130
|
+
// Solomon approved — proceed
|
|
131
|
+
return { action: "ok" };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
session.last_reviewer_feedback = `Security feedback: ${securityOutput.summary}`;
|
|
135
|
+
await saveSession(session);
|
|
136
|
+
return { action: "continue" };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
session.security_retry_count = 0;
|
|
140
|
+
return { action: "ok", stageResult: { ok: true, summary: securityOutput.summary || "No vulnerabilities found" } };
|
|
141
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { TriageRole } from "../roles/triage-role.js";
|
|
2
|
+
import { ResearcherRole } from "../roles/researcher-role.js";
|
|
3
|
+
import { PlannerRole } from "../roles/planner-role.js";
|
|
4
|
+
import { createAgent } from "../agents/index.js";
|
|
5
|
+
import { addCheckpoint, markSessionStatus } from "../session-store.js";
|
|
6
|
+
import { emitProgress, makeEvent } from "../utils/events.js";
|
|
7
|
+
import { parsePlannerOutput } from "../prompts/planner.js";
|
|
8
|
+
|
|
9
|
+
export async function runTriageStage({ config, logger, emitter, eventBase, session, coderRole, trackBudget }) {
|
|
10
|
+
logger.setContext({ iteration: 0, stage: "triage" });
|
|
11
|
+
emitProgress(
|
|
12
|
+
emitter,
|
|
13
|
+
makeEvent("triage:start", { ...eventBase, stage: "triage" }, {
|
|
14
|
+
message: "Triage classifying task complexity"
|
|
15
|
+
})
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const triage = new TriageRole({ config, logger, emitter });
|
|
19
|
+
await triage.init({ task: session.task, sessionId: session.id, iteration: 0 });
|
|
20
|
+
const triageStart = Date.now();
|
|
21
|
+
const triageOutput = await triage.run({ task: session.task });
|
|
22
|
+
trackBudget({
|
|
23
|
+
role: "triage",
|
|
24
|
+
provider: config?.roles?.triage?.provider || coderRole.provider,
|
|
25
|
+
model: config?.roles?.triage?.model || coderRole.model,
|
|
26
|
+
result: triageOutput,
|
|
27
|
+
duration_ms: Date.now() - triageStart
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
await addCheckpoint(session, { stage: "triage", iteration: 0, ok: triageOutput.ok });
|
|
31
|
+
|
|
32
|
+
const recommendedRoles = new Set(triageOutput.result?.roles || []);
|
|
33
|
+
const roleOverrides = {};
|
|
34
|
+
if (triageOutput.ok) {
|
|
35
|
+
roleOverrides.plannerEnabled = recommendedRoles.has("planner");
|
|
36
|
+
roleOverrides.researcherEnabled = recommendedRoles.has("researcher");
|
|
37
|
+
roleOverrides.refactorerEnabled = recommendedRoles.has("refactorer");
|
|
38
|
+
roleOverrides.reviewerEnabled = recommendedRoles.has("reviewer");
|
|
39
|
+
roleOverrides.testerEnabled = recommendedRoles.has("tester");
|
|
40
|
+
roleOverrides.securityEnabled = recommendedRoles.has("security");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const stageResult = {
|
|
44
|
+
ok: triageOutput.ok,
|
|
45
|
+
level: triageOutput.result?.level || null,
|
|
46
|
+
roles: Array.from(recommendedRoles),
|
|
47
|
+
reasoning: triageOutput.result?.reasoning || null
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
emitProgress(
|
|
51
|
+
emitter,
|
|
52
|
+
makeEvent("triage:end", { ...eventBase, stage: "triage" }, {
|
|
53
|
+
status: triageOutput.ok ? "ok" : "fail",
|
|
54
|
+
message: triageOutput.ok ? "Triage completed" : `Triage failed: ${triageOutput.summary}`,
|
|
55
|
+
detail: stageResult
|
|
56
|
+
})
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
return { roleOverrides, stageResult };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function runResearcherStage({ config, logger, emitter, eventBase, session, coderRole, trackBudget }) {
|
|
63
|
+
logger.setContext({ iteration: 0, stage: "researcher" });
|
|
64
|
+
emitProgress(
|
|
65
|
+
emitter,
|
|
66
|
+
makeEvent("researcher:start", { ...eventBase, stage: "researcher" }, {
|
|
67
|
+
message: "Researcher investigating codebase"
|
|
68
|
+
})
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const researcher = new ResearcherRole({ config, logger, emitter });
|
|
72
|
+
await researcher.init({ task: session.task });
|
|
73
|
+
const researchStart = Date.now();
|
|
74
|
+
const researchOutput = await researcher.run({ task: session.task });
|
|
75
|
+
trackBudget({
|
|
76
|
+
role: "researcher",
|
|
77
|
+
provider: config?.roles?.researcher?.provider || coderRole.provider,
|
|
78
|
+
model: config?.roles?.researcher?.model || coderRole.model,
|
|
79
|
+
result: researchOutput,
|
|
80
|
+
duration_ms: Date.now() - researchStart
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
await addCheckpoint(session, { stage: "researcher", iteration: 0, ok: researchOutput.ok });
|
|
84
|
+
|
|
85
|
+
emitProgress(
|
|
86
|
+
emitter,
|
|
87
|
+
makeEvent("researcher:end", { ...eventBase, stage: "researcher" }, {
|
|
88
|
+
status: researchOutput.ok ? "ok" : "fail",
|
|
89
|
+
message: researchOutput.ok ? "Research completed" : `Research failed: ${researchOutput.summary}`
|
|
90
|
+
})
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const stageResult = { ok: researchOutput.ok, summary: researchOutput.summary || null };
|
|
94
|
+
const researchContext = researchOutput.ok ? researchOutput.result : null;
|
|
95
|
+
|
|
96
|
+
return { researchContext, stageResult };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export async function runPlannerStage({ config, logger, emitter, eventBase, session, plannerRole, researchContext, trackBudget }) {
|
|
100
|
+
const task = session.task;
|
|
101
|
+
logger.setContext({ iteration: 0, stage: "planner" });
|
|
102
|
+
emitProgress(
|
|
103
|
+
emitter,
|
|
104
|
+
makeEvent("planner:start", { ...eventBase, stage: "planner" }, {
|
|
105
|
+
message: `Planner (${plannerRole.provider}) running`,
|
|
106
|
+
detail: { planner: plannerRole.provider }
|
|
107
|
+
})
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const planRole = new PlannerRole({ config, logger, emitter, createAgentFn: createAgent });
|
|
111
|
+
planRole.context = { task, research: researchContext };
|
|
112
|
+
await planRole.init();
|
|
113
|
+
const plannerStart = Date.now();
|
|
114
|
+
const planResult = await planRole.execute(task);
|
|
115
|
+
trackBudget({ role: "planner", provider: plannerRole.provider, model: plannerRole.model, result: planResult.result, duration_ms: Date.now() - plannerStart });
|
|
116
|
+
|
|
117
|
+
if (!planResult.ok) {
|
|
118
|
+
await markSessionStatus(session, "failed");
|
|
119
|
+
const details = planResult.result?.error || planResult.summary || "unknown error";
|
|
120
|
+
emitProgress(
|
|
121
|
+
emitter,
|
|
122
|
+
makeEvent("planner:end", { ...eventBase, stage: "planner" }, {
|
|
123
|
+
status: "fail",
|
|
124
|
+
message: `Planner failed: ${details}`
|
|
125
|
+
})
|
|
126
|
+
);
|
|
127
|
+
throw new Error(`Planner failed: ${details}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const planOutput = planResult.result?.plan || "";
|
|
131
|
+
const plannedTask = planOutput ? `${task}\n\nExecution plan:\n${planOutput}` : task;
|
|
132
|
+
const parsedPlan = parsePlannerOutput(planOutput);
|
|
133
|
+
const stageResult = {
|
|
134
|
+
ok: true,
|
|
135
|
+
title: parsedPlan?.title || null,
|
|
136
|
+
approach: parsedPlan?.approach || null,
|
|
137
|
+
steps: parsedPlan?.steps || [],
|
|
138
|
+
completedSteps: []
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
emitProgress(
|
|
142
|
+
emitter,
|
|
143
|
+
makeEvent("planner:end", { ...eventBase, stage: "planner" }, {
|
|
144
|
+
message: "Planner completed"
|
|
145
|
+
})
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
return { plannedTask, stageResult };
|
|
149
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ReviewerRole } from "../roles/reviewer-role.js";
|
|
2
|
+
import { createAgent } from "../agents/index.js";
|
|
3
|
+
import { addCheckpoint } from "../session-store.js";
|
|
4
|
+
|
|
5
|
+
export async function runReviewerWithFallback({ reviewerName, config, logger, emitter, reviewInput, session, iteration, onAttemptResult }) {
|
|
6
|
+
const fallbackReviewer = config.reviewer_options?.fallback_reviewer;
|
|
7
|
+
const retries = Math.max(0, Number(config.reviewer_options?.retries ?? 1));
|
|
8
|
+
const candidates = [reviewerName];
|
|
9
|
+
if (fallbackReviewer && fallbackReviewer !== reviewerName) {
|
|
10
|
+
candidates.push(fallbackReviewer);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const attempts = [];
|
|
14
|
+
for (const name of candidates) {
|
|
15
|
+
const reviewerConfig = { ...config, roles: { ...config.roles, reviewer: { ...config.roles?.reviewer, provider: name } } };
|
|
16
|
+
const role = new ReviewerRole({ config: reviewerConfig, logger, emitter, createAgentFn: createAgent });
|
|
17
|
+
await role.init();
|
|
18
|
+
for (let attempt = 1; attempt <= retries + 1; attempt += 1) {
|
|
19
|
+
const execResult = await role.execute(reviewInput);
|
|
20
|
+
if (onAttemptResult) {
|
|
21
|
+
await onAttemptResult({ reviewer: name, result: execResult.result });
|
|
22
|
+
}
|
|
23
|
+
attempts.push({ reviewer: name, attempt, ok: execResult.ok, result: execResult.result, execResult });
|
|
24
|
+
await addCheckpoint(session, {
|
|
25
|
+
stage: "reviewer-attempt",
|
|
26
|
+
iteration,
|
|
27
|
+
reviewer: name,
|
|
28
|
+
attempt,
|
|
29
|
+
ok: execResult.ok
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
if (execResult.ok) {
|
|
33
|
+
return { execResult, attempts };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return { execResult: null, attempts };
|
|
39
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { SolomonRole } from "../roles/solomon-role.js";
|
|
2
|
+
import { addCheckpoint, pauseSession } from "../session-store.js";
|
|
3
|
+
import { emitProgress, makeEvent } from "../utils/events.js";
|
|
4
|
+
|
|
5
|
+
export async function invokeSolomon({ config, logger, emitter, eventBase, stage, conflict, askQuestion, session, iteration }) {
|
|
6
|
+
const solomonEnabled = Boolean(config.pipeline?.solomon?.enabled);
|
|
7
|
+
|
|
8
|
+
if (!solomonEnabled) {
|
|
9
|
+
return escalateToHuman({ askQuestion, session, emitter, eventBase, stage, conflict, iteration });
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
emitProgress(
|
|
13
|
+
emitter,
|
|
14
|
+
makeEvent("solomon:start", { ...eventBase, stage: "solomon" }, {
|
|
15
|
+
message: `Solomon arbitrating ${stage} conflict`,
|
|
16
|
+
detail: { conflictStage: stage }
|
|
17
|
+
})
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const solomon = new SolomonRole({ config, logger, emitter });
|
|
21
|
+
await solomon.init({ task: conflict.task || session.task, iteration });
|
|
22
|
+
const ruling = await solomon.run({ conflict });
|
|
23
|
+
|
|
24
|
+
emitProgress(
|
|
25
|
+
emitter,
|
|
26
|
+
makeEvent("solomon:end", { ...eventBase, stage: "solomon" }, {
|
|
27
|
+
message: `Solomon ruling: ${ruling.result?.ruling || "unknown"}`,
|
|
28
|
+
detail: ruling.result
|
|
29
|
+
})
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
await addCheckpoint(session, {
|
|
33
|
+
stage: "solomon",
|
|
34
|
+
iteration,
|
|
35
|
+
ruling: ruling.result?.ruling,
|
|
36
|
+
escalate: ruling.result?.escalate,
|
|
37
|
+
subtask: ruling.result?.subtask?.title || null
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (!ruling.ok) {
|
|
41
|
+
return escalateToHuman({
|
|
42
|
+
askQuestion, session, emitter, eventBase, stage, iteration,
|
|
43
|
+
conflict: { ...conflict, solomonReason: ruling.result?.escalate_reason }
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const r = ruling.result?.ruling;
|
|
48
|
+
if (r === "approve" || r === "approve_with_conditions") {
|
|
49
|
+
return { action: "continue", conditions: ruling.result?.conditions || [], ruling };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (r === "create_subtask") {
|
|
53
|
+
return { action: "subtask", subtask: ruling.result?.subtask, ruling };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return { action: "continue", conditions: [], ruling };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function escalateToHuman({ askQuestion, session, emitter, eventBase, stage, conflict, iteration }) {
|
|
60
|
+
const reason = conflict?.solomonReason || `${stage} conflict unresolved`;
|
|
61
|
+
const question = `${stage} conflict requires human intervention: ${reason}\nDetails: ${JSON.stringify(conflict?.history?.slice(-2) || [], null, 2)}\n\nHow should we proceed?`;
|
|
62
|
+
|
|
63
|
+
if (askQuestion) {
|
|
64
|
+
const answer = await askQuestion(question, { iteration, stage });
|
|
65
|
+
if (answer) {
|
|
66
|
+
return { action: "continue", humanGuidance: answer };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
await pauseSession(session, {
|
|
71
|
+
question,
|
|
72
|
+
context: { iteration, stage, conflict }
|
|
73
|
+
});
|
|
74
|
+
emitProgress(
|
|
75
|
+
emitter,
|
|
76
|
+
makeEvent("question", { ...eventBase, stage }, {
|
|
77
|
+
status: "paused",
|
|
78
|
+
message: question,
|
|
79
|
+
detail: { question, sessionId: session.id }
|
|
80
|
+
})
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
return { action: "pause", question };
|
|
84
|
+
}
|