@navels/neal 0.1.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/LICENSE +21 -0
- package/README.md +527 -0
- package/SECURITY.md +91 -0
- package/config.yml +104 -0
- package/dist/neal/activity-footer.js +177 -0
- package/dist/neal/activity-reporting.js +1 -0
- package/dist/neal/adjudicator/artifacts.js +58 -0
- package/dist/neal/adjudicator/blocked-adjudicator.js +223 -0
- package/dist/neal/adjudicator/contracts.js +139 -0
- package/dist/neal/adjudicator/execute.js +611 -0
- package/dist/neal/adjudicator/final-completion.js +104 -0
- package/dist/neal/adjudicator/planning.js +145 -0
- package/dist/neal/adjudicator/specs.js +453 -0
- package/dist/neal/agents/prompts.js +120 -0
- package/dist/neal/agents/rounds.js +706 -0
- package/dist/neal/agents/schemas.js +832 -0
- package/dist/neal/agents/structured-coder.js +82 -0
- package/dist/neal/agents/structured-json.js +528 -0
- package/dist/neal/agents.js +4 -0
- package/dist/neal/atomic-write.js +18 -0
- package/dist/neal/blocked-guidance.js +406 -0
- package/dist/neal/cli.js +471 -0
- package/dist/neal/commands/check.js +401 -0
- package/dist/neal/commands/compat.js +807 -0
- package/dist/neal/commands/interactive-activity.js +57 -0
- package/dist/neal/commands/new-run.js +79 -0
- package/dist/neal/commands/plan-and-execute.js +44 -0
- package/dist/neal/commands/recovery-guidance.js +217 -0
- package/dist/neal/commands/resume-run.js +395 -0
- package/dist/neal/commands/review.js +21 -0
- package/dist/neal/commands/runtime.js +557 -0
- package/dist/neal/commands/setup.js +596 -0
- package/dist/neal/commands/squash.js +113 -0
- package/dist/neal/commands/status.js +33 -0
- package/dist/neal/commands/writer-exit-codes.js +42 -0
- package/dist/neal/commit-message.js +17 -0
- package/dist/neal/config.js +432 -0
- package/dist/neal/context/artifacts.js +140 -0
- package/dist/neal/context/context.js +324 -0
- package/dist/neal/context/inline-review-context.js +131 -0
- package/dist/neal/context/reviewer-context.js +166 -0
- package/dist/neal/context/shared.js +117 -0
- package/dist/neal/context/types.js +1 -0
- package/dist/neal/diagnostic.js +208 -0
- package/dist/neal/execute-finalization.js +5 -0
- package/dist/neal/final-completion-review.js +188 -0
- package/dist/neal/final-completion.js +229 -0
- package/dist/neal/git.js +339 -0
- package/dist/neal/index.js +135 -0
- package/dist/neal/interactive-controls.js +85 -0
- package/dist/neal/logger.js +102 -0
- package/dist/neal/manual-gates.js +121 -0
- package/dist/neal/orchestrator/artifacts.js +70 -0
- package/dist/neal/orchestrator/completion.js +531 -0
- package/dist/neal/orchestrator/failures.js +31 -0
- package/dist/neal/orchestrator/notifications.js +175 -0
- package/dist/neal/orchestrator/phases/coder.js +516 -0
- package/dist/neal/orchestrator/phases/planning.js +540 -0
- package/dist/neal/orchestrator/phases/recovery.js +798 -0
- package/dist/neal/orchestrator/phases/review.js +136 -0
- package/dist/neal/orchestrator/phases/shared.js +279 -0
- package/dist/neal/orchestrator/run-loop.js +113 -0
- package/dist/neal/orchestrator/split-plan.js +235 -0
- package/dist/neal/orchestrator/transitions.js +309 -0
- package/dist/neal/orchestrator.js +215 -0
- package/dist/neal/phase-display.js +27 -0
- package/dist/neal/plan-doc.js +154 -0
- package/dist/neal/plan-queue.js +1092 -0
- package/dist/neal/plan-refinement.js +39 -0
- package/dist/neal/plan-validation.js +525 -0
- package/dist/neal/progress.js +237 -0
- package/dist/neal/prompts/assert-builder.js +13 -0
- package/dist/neal/prompts/execute.js +290 -0
- package/dist/neal/prompts/guidance.js +70 -0
- package/dist/neal/prompts/planning.js +313 -0
- package/dist/neal/prompts/review-doctrine.js +142 -0
- package/dist/neal/prompts/shared.js +101 -0
- package/dist/neal/prompts/specialized.js +212 -0
- package/dist/neal/prompts/specs.js +572 -0
- package/dist/neal/providers/anthropic-claude.js +1599 -0
- package/dist/neal/providers/detection.js +139 -0
- package/dist/neal/providers/generic-agentic-tools.js +586 -0
- package/dist/neal/providers/generic-agentic.js +1238 -0
- package/dist/neal/providers/liveness.js +151 -0
- package/dist/neal/providers/openai-codex.js +1014 -0
- package/dist/neal/providers/openai-compatible.js +654 -0
- package/dist/neal/providers/registry.js +389 -0
- package/dist/neal/providers/telemetry.js +208 -0
- package/dist/neal/providers/types.js +21 -0
- package/dist/neal/recovery-artifacts.js +50 -0
- package/dist/neal/resume-decision.js +220 -0
- package/dist/neal/resume-planner.js +265 -0
- package/dist/neal/retrospective.js +391 -0
- package/dist/neal/review-debt.js +18 -0
- package/dist/neal/review-findings/artifacts.js +173 -0
- package/dist/neal/review-findings/prompts.js +172 -0
- package/dist/neal/review-findings/provider.js +330 -0
- package/dist/neal/review-findings/run.js +373 -0
- package/dist/neal/review-findings/types.js +1 -0
- package/dist/neal/review-mode.js +67 -0
- package/dist/neal/review.js +137 -0
- package/dist/neal/run-lock.js +334 -0
- package/dist/neal/run-metrics.js +355 -0
- package/dist/neal/run-narrative-types.js +1 -0
- package/dist/neal/run-narrative.js +1374 -0
- package/dist/neal/run-registry.js +218 -0
- package/dist/neal/run-status.js +25 -0
- package/dist/neal/scopes.js +451 -0
- package/dist/neal/sensitive-text.js +8 -0
- package/dist/neal/squash-message.js +379 -0
- package/dist/neal/squash.js +591 -0
- package/dist/neal/state-invariants.js +496 -0
- package/dist/neal/state-views.js +344 -0
- package/dist/neal/state.js +887 -0
- package/dist/neal/status-footer.js +258 -0
- package/dist/neal/status.js +1260 -0
- package/dist/neal/storage-paths.js +57 -0
- package/dist/neal/support.js +58 -0
- package/dist/neal/terminal-narrator.js +435 -0
- package/dist/neal/types.js +1 -0
- package/dist/neal/verification-events.js +81 -0
- package/dist/neal/version.js +37 -0
- package/dist/neal/worktree-status.js +137 -0
- package/dist/notifier.js +44 -0
- package/docs/ADJUDICATOR_INVENTORY.md +310 -0
- package/docs/PROMPT_SPECS.md +266 -0
- package/docs/README.md +22 -0
- package/docs/architecture.md +113 -0
- package/docs/assets/neal-execution-flow.png +0 -0
- package/docs/automation.md +65 -0
- package/docs/comparison.md +105 -0
- package/docs/compat.md +269 -0
- package/docs/compatible-models.md +135 -0
- package/docs/demo.md +55 -0
- package/docs/maintenance.md +64 -0
- package/docs/plan-format.md +213 -0
- package/docs/providers.md +751 -0
- package/docs/release.md +147 -0
- package/docs/state-machine.md +266 -0
- package/docs/storage.md +207 -0
- package/docs/troubleshooting.md +152 -0
- package/examples/compat/add-edit-verify/PLAN.md +29 -0
- package/examples/compat/add-edit-verify/broken.diff +8 -0
- package/examples/compat/add-edit-verify/good.diff +8 -0
- package/examples/compat/add-edit-verify/package.json +5 -0
- package/examples/compat/add-edit-verify/src/add.js +2 -0
- package/examples/compat/add-edit-verify/test/add.test.js +9 -0
- package/examples/compat/is-even-add-test/PLAN.md +30 -0
- package/examples/compat/is-even-add-test/broken.diff +11 -0
- package/examples/compat/is-even-add-test/good.diff +11 -0
- package/examples/compat/is-even-add-test/package.json +5 -0
- package/examples/compat/is-even-add-test/src/is-even.js +3 -0
- package/examples/compat/is-even-add-test/test/is-even.test.js +9 -0
- package/examples/compat/manifest.json +60 -0
- package/examples/compat/plan-greeting/ISSUE.md +25 -0
- package/examples/compat/plan-greeting/package.json +5 -0
- package/examples/compat/plan-greeting/src/greet.js +2 -0
- package/examples/compat/plan-greeting/test/greet.test.js +8 -0
- package/examples/compat/reverse-grep-edit/PLAN.md +32 -0
- package/examples/compat/reverse-grep-edit/broken.diff +12 -0
- package/examples/compat/reverse-grep-edit/good.diff +12 -0
- package/examples/compat/reverse-grep-edit/package.json +5 -0
- package/examples/compat/reverse-grep-edit/src/strings.js +10 -0
- package/examples/compat/reverse-grep-edit/test/strings.test.js +15 -0
- package/examples/issue-triage-js/PLAN.md +83 -0
- package/examples/issue-triage-js/README.md +76 -0
- package/examples/issue-triage-js/package.json +9 -0
- package/examples/issue-triage-js/src/issue-triage.js +87 -0
- package/examples/issue-triage-js/test/issue-triage.test.js +107 -0
- package/package.json +70 -0
|
@@ -0,0 +1,706 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { getAgentTurnRetryLimit, getAgentTurnStartupTimeoutMs, getApiRetryLimit, getInactivityTimeoutMs, } from '../config.js';
|
|
3
|
+
import { runWithAgentTurnLiveness } from '../providers/liveness.js';
|
|
4
|
+
import { normalizeExecutionShapeDeclaration } from '../plan-validation.js';
|
|
5
|
+
import { getCoderAdapter, getStructuredAdvisorAdapter } from '../providers/registry.js';
|
|
6
|
+
import { createProviderTelemetrySink } from '../providers/telemetry.js';
|
|
7
|
+
import { isNealProviderError, NealProviderError } from '../providers/types.js';
|
|
8
|
+
import { getReviewerDoctrineAccessMode, reviewerNeedsInlineContext, } from '../context/inline-review-context.js';
|
|
9
|
+
import { AUTONOMY_BLOCKED, AUTONOMY_CHUNK_DONE, AUTONOMY_DONE, AUTONOMY_SCOPE_DONE, AUTONOMY_SPLIT_PLAN, buildBlockedRecoveryCoderPrompt, buildCoderResponsePrompt, buildCoderPlanResponsePrompt, buildFinalCompletionReviewerPrompt, buildFinalCompletionSummaryPrompt, buildLegacyScopePrompt, buildLegacyPlanningPrompt, buildPlanReviewerPrompt, buildPlanningPrompt, buildReviewerPrompt, buildBlockedAdjudicatorPrompt, buildScopePrompt, } from './prompts.js';
|
|
10
|
+
import { buildCoderBlockedRecoveryDispositionSchema, buildCoderPlanSchema, buildCoderScopeSchema, buildCoderPlanResponseSchema, buildCoderResponseSchema, buildBlockedAdjudicatorSchema, buildFinalCompletionSummarySchema, buildFinalCompletionReviewerSchema, buildPlanReviewerSchema, buildReviewerSchema, parseExecuteScopeProgressPayload, parseFinalCompletionReviewerPayload, parseFinalCompletionSummaryPayload, stripExecuteScopeProgressPayload, validateCoderBlockedRecoveryDispositionPayload, validateCoderPlanPayload, validateCoderPlanResponsePayload, validateCoderResponsePayload, validateCoderScopePayload, validatePlanReviewerPayload, validateBlockedAdjudicatorVerdictPayload, validateReviewerPayload, } from './schemas.js';
|
|
11
|
+
import { runCoderStructuredPrompt, translateCoderProviderError } from './structured-coder.js';
|
|
12
|
+
export class ReviewerRoundError extends Error {
|
|
13
|
+
sessionHandle;
|
|
14
|
+
providerError;
|
|
15
|
+
kind;
|
|
16
|
+
retryable;
|
|
17
|
+
subtype;
|
|
18
|
+
constructor(providerError) {
|
|
19
|
+
super(providerError.message);
|
|
20
|
+
this.name = 'ReviewerRoundError';
|
|
21
|
+
this.sessionHandle = providerError.sessionHandle;
|
|
22
|
+
this.providerError = providerError;
|
|
23
|
+
this.kind = providerError.kind;
|
|
24
|
+
this.retryable = providerError.retryable;
|
|
25
|
+
this.subtype = providerError.kind;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function validationErrorMessage(error) {
|
|
29
|
+
return error instanceof Error ? error.message : String(error);
|
|
30
|
+
}
|
|
31
|
+
export class FinalCompletionReviewerVerdictError extends Error {
|
|
32
|
+
originalValidationMessage;
|
|
33
|
+
sessionHandle;
|
|
34
|
+
subtype;
|
|
35
|
+
cause;
|
|
36
|
+
constructor(args) {
|
|
37
|
+
const message = validationErrorMessage(args.cause);
|
|
38
|
+
super(message, { cause: args.cause });
|
|
39
|
+
this.name = 'FinalCompletionReviewerVerdictError';
|
|
40
|
+
this.originalValidationMessage = message;
|
|
41
|
+
this.sessionHandle = args.sessionHandle;
|
|
42
|
+
this.subtype = 'final_completion_verdict_invalid';
|
|
43
|
+
this.cause = args.cause;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Legacy marker-protocol compatibility for active legacy_marker_v1 primary
|
|
47
|
+
// sessions. New primary planning/execution sessions use structured envelopes.
|
|
48
|
+
function extractMarker(message) {
|
|
49
|
+
for (const rawLine of message.split(/\r?\n/)) {
|
|
50
|
+
const line = rawLine.trim();
|
|
51
|
+
if (line === AUTONOMY_SCOPE_DONE ||
|
|
52
|
+
line === AUTONOMY_CHUNK_DONE ||
|
|
53
|
+
line === AUTONOMY_DONE ||
|
|
54
|
+
line === AUTONOMY_BLOCKED ||
|
|
55
|
+
line === AUTONOMY_SPLIT_PLAN) {
|
|
56
|
+
return line;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
function translateReviewerProviderError(error) {
|
|
62
|
+
if (isNealProviderError(error)) {
|
|
63
|
+
return new ReviewerRoundError(error);
|
|
64
|
+
}
|
|
65
|
+
return error;
|
|
66
|
+
}
|
|
67
|
+
async function safeReadText(path) {
|
|
68
|
+
try {
|
|
69
|
+
return await readFile(path, 'utf8');
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return '';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function getCoderRuntimeOptions(cwd) {
|
|
76
|
+
return {
|
|
77
|
+
inactivityTimeoutMs: getInactivityTimeoutMs(cwd),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function getStructuredAdvisorRuntimeOptions(cwd) {
|
|
81
|
+
return {
|
|
82
|
+
inactivityTimeoutMs: getInactivityTimeoutMs(cwd),
|
|
83
|
+
apiRetryLimit: getApiRetryLimit(cwd),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function buildStructuredJsonProtocolSpec(args) {
|
|
87
|
+
return {
|
|
88
|
+
protocol: 'neal-json-block-v1',
|
|
89
|
+
schemaLabel: args.schemaLabel,
|
|
90
|
+
schema: args.schema,
|
|
91
|
+
validator: args.validator,
|
|
92
|
+
repairAttemptLimit: args.repairAttemptLimit ?? 2,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function createCoderProviderEventSink(args) {
|
|
96
|
+
return createProviderTelemetrySink({
|
|
97
|
+
logger: args.logger,
|
|
98
|
+
provider: args.coder.provider,
|
|
99
|
+
role: 'coder',
|
|
100
|
+
...(args.label ? { label: args.label } : {}),
|
|
101
|
+
cwd: args.cwd,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
function createStructuredAdvisorProviderEventSink(args) {
|
|
105
|
+
return createProviderTelemetrySink({
|
|
106
|
+
logger: args.logger,
|
|
107
|
+
provider: args.advisor.provider,
|
|
108
|
+
role: 'structured-advisor',
|
|
109
|
+
label: args.label,
|
|
110
|
+
cwd: args.cwd,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// Fail-fast guard for reviewer rounds whose prompts still contain
|
|
114
|
+
// repository/tool-access instructions. A reviewer provider without read tool
|
|
115
|
+
// access must never receive such a prompt: either the round supplies
|
|
116
|
+
// Neal-inlined reviewer context (inlineContextSupplied), or the round must
|
|
117
|
+
// reject the configuration with a clear error instead of silently sending
|
|
118
|
+
// instructions the provider cannot follow. Every reviewer round label now
|
|
119
|
+
// supports inline context, but a call site that fails to supply it for a
|
|
120
|
+
// no-read reviewer must still fail fast here.
|
|
121
|
+
//
|
|
122
|
+
// The guard throws a ReviewerRoundError backed by a non-retryable
|
|
123
|
+
// NealProviderError with sessionHandle: null so it flows through the same
|
|
124
|
+
// orchestrator reviewer failure path as any other reviewer round failure
|
|
125
|
+
// (persisted failed state, written artifacts, logged phase errors).
|
|
126
|
+
function assertReviewerRoundSupportsNoReadReviewer(args) {
|
|
127
|
+
if (args.inlineContextSupplied || !reviewerNeedsInlineContext(args.reviewer)) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
throw new ReviewerRoundError(new NealProviderError({
|
|
131
|
+
message: [
|
|
132
|
+
`Reviewer provider ${JSON.stringify(args.reviewer.provider)} has no repository read access,`,
|
|
133
|
+
`and the ${args.label} reviewer round did not receive Neal-inlined reviewer context,`,
|
|
134
|
+
'so its prompt would contain repository-access instructions the provider cannot follow.',
|
|
135
|
+
'Configure a read-capable reviewer provider (agent.reviewer.provider) for runs that reach',
|
|
136
|
+
`${args.label} rounds until this round supports inline reviewer context.`,
|
|
137
|
+
].join(' '),
|
|
138
|
+
provider: args.reviewer.provider,
|
|
139
|
+
role: 'structured-advisor',
|
|
140
|
+
sessionHandle: null,
|
|
141
|
+
kind: 'provider_failed',
|
|
142
|
+
retryable: false,
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
async function runReviewerStructuredRound(args) {
|
|
146
|
+
try {
|
|
147
|
+
const advisor = getStructuredAdvisorAdapter(args.reviewer);
|
|
148
|
+
const result = await runWithAgentTurnLiveness({
|
|
149
|
+
provider: args.reviewer.provider,
|
|
150
|
+
role: 'structured-advisor',
|
|
151
|
+
label: args.label,
|
|
152
|
+
startupTimeoutMs: Math.min(getAgentTurnStartupTimeoutMs(args.cwd), getInactivityTimeoutMs(args.cwd)),
|
|
153
|
+
// Structured-advisor turns retry locally: they are non-writing by policy
|
|
154
|
+
// and do not resume sessions at this call site.
|
|
155
|
+
retryLimit: getAgentTurnRetryLimit(args.cwd),
|
|
156
|
+
logger: args.logger,
|
|
157
|
+
baseSink: createStructuredAdvisorProviderEventSink({
|
|
158
|
+
advisor: args.reviewer,
|
|
159
|
+
cwd: args.cwd,
|
|
160
|
+
label: args.label,
|
|
161
|
+
logger: args.logger,
|
|
162
|
+
}),
|
|
163
|
+
run: (events, attempt) => advisor.runStructuredRound({
|
|
164
|
+
label: args.label,
|
|
165
|
+
cwd: args.cwd,
|
|
166
|
+
prompt: args.prompt,
|
|
167
|
+
schema: args.schema,
|
|
168
|
+
structuredJsonProtocol: args.structuredJsonProtocol,
|
|
169
|
+
// Liveness retry sits outside the adapter-owned apiRetryLimit; it
|
|
170
|
+
// does not replace provider API retry.
|
|
171
|
+
...getStructuredAdvisorRuntimeOptions(args.cwd),
|
|
172
|
+
signal: attempt.signal,
|
|
173
|
+
events,
|
|
174
|
+
}),
|
|
175
|
+
});
|
|
176
|
+
return {
|
|
177
|
+
sessionHandle: result.sessionHandle,
|
|
178
|
+
structured: result.structured,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
throw translateReviewerProviderError(error);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
export async function runReviewerRound(args) {
|
|
186
|
+
assertReviewerRoundSupportsNoReadReviewer({
|
|
187
|
+
reviewer: args.reviewer,
|
|
188
|
+
label: 'review',
|
|
189
|
+
inlineContextSupplied: Boolean(args.inlineContext),
|
|
190
|
+
});
|
|
191
|
+
const schema = buildReviewerSchema();
|
|
192
|
+
const { sessionHandle, structured } = await runReviewerStructuredRound({
|
|
193
|
+
reviewer: args.reviewer,
|
|
194
|
+
label: 'review',
|
|
195
|
+
cwd: args.cwd,
|
|
196
|
+
// The doctrine access mode comes from the reviewer provider's declared
|
|
197
|
+
// structured-advisor tool access, not from inline-context presence alone.
|
|
198
|
+
prompt: buildReviewerPrompt({ ...args, accessMode: getReviewerDoctrineAccessMode(args.reviewer) }),
|
|
199
|
+
schema,
|
|
200
|
+
structuredJsonProtocol: buildStructuredJsonProtocolSpec({
|
|
201
|
+
schemaLabel: 'reviewer_payload',
|
|
202
|
+
schema,
|
|
203
|
+
validator: validateReviewerPayload,
|
|
204
|
+
}),
|
|
205
|
+
logger: args.logger,
|
|
206
|
+
});
|
|
207
|
+
return {
|
|
208
|
+
sessionHandle,
|
|
209
|
+
summary: structured.summary,
|
|
210
|
+
findings: structured.findings.map((finding) => ({
|
|
211
|
+
round: args.round,
|
|
212
|
+
source: 'reviewer',
|
|
213
|
+
severity: finding.severity,
|
|
214
|
+
files: finding.files,
|
|
215
|
+
claim: finding.claim,
|
|
216
|
+
evidence: finding.evidence,
|
|
217
|
+
requiredAction: finding.requiredAction,
|
|
218
|
+
roundSummary: structured.summary,
|
|
219
|
+
})),
|
|
220
|
+
meaningfulProgress: {
|
|
221
|
+
action: structured.meaningfulProgressAction,
|
|
222
|
+
rationale: structured.meaningfulProgressRationale,
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
export async function runPlanReviewerRound(args) {
|
|
227
|
+
assertReviewerRoundSupportsNoReadReviewer({
|
|
228
|
+
reviewer: args.reviewer,
|
|
229
|
+
label: 'plan-review',
|
|
230
|
+
inlineContextSupplied: Boolean(args.inlineContext),
|
|
231
|
+
});
|
|
232
|
+
const schema = buildPlanReviewerSchema();
|
|
233
|
+
const { sessionHandle, structured } = await runReviewerStructuredRound({
|
|
234
|
+
reviewer: args.reviewer,
|
|
235
|
+
label: 'plan-review',
|
|
236
|
+
cwd: args.cwd,
|
|
237
|
+
// The doctrine access mode comes from the reviewer provider's declared
|
|
238
|
+
// structured-advisor tool access, not from inline-context presence alone.
|
|
239
|
+
prompt: buildPlanReviewerPrompt({ ...args, accessMode: getReviewerDoctrineAccessMode(args.reviewer) }),
|
|
240
|
+
schema,
|
|
241
|
+
structuredJsonProtocol: buildStructuredJsonProtocolSpec({
|
|
242
|
+
schemaLabel: 'plan_reviewer_payload',
|
|
243
|
+
schema,
|
|
244
|
+
validator: validatePlanReviewerPayload,
|
|
245
|
+
}),
|
|
246
|
+
logger: args.logger,
|
|
247
|
+
});
|
|
248
|
+
return {
|
|
249
|
+
sessionHandle,
|
|
250
|
+
summary: structured.summary,
|
|
251
|
+
executionShape: structured.executionShape,
|
|
252
|
+
findings: structured.findings.map((finding) => ({
|
|
253
|
+
round: args.round,
|
|
254
|
+
source: 'reviewer',
|
|
255
|
+
severity: finding.severity,
|
|
256
|
+
files: finding.files,
|
|
257
|
+
claim: finding.claim,
|
|
258
|
+
requiredAction: finding.requiredAction,
|
|
259
|
+
roundSummary: structured.summary,
|
|
260
|
+
})),
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
// Read-only, inline-only round for the review_stuck arbiter. It runs through the
|
|
264
|
+
// same runReviewerStructuredRound plumbing the support / final-completion
|
|
265
|
+
// reviewers use (read-only tools only, zero commits, zero file edits). Because
|
|
266
|
+
// the arbiter judges entirely from Neal-inlined in-memory artifacts (plan, open
|
|
267
|
+
// blocking findings, reviewer-round snapshots), it has a single no-read-safe
|
|
268
|
+
// prompt variant and therefore requires a non-null inlineContext. The guard
|
|
269
|
+
// fires for a no-read reviewer paired with a null context before any adapter
|
|
270
|
+
// contact; the explicit null check below additionally covers a read-capable
|
|
271
|
+
// reviewer that was handed a null context (the adjudicator always passes a
|
|
272
|
+
// non-null context in production).
|
|
273
|
+
export async function runBlockedAdjudicatorRound(args) {
|
|
274
|
+
assertReviewerRoundSupportsNoReadReviewer({
|
|
275
|
+
reviewer: args.reviewer,
|
|
276
|
+
label: 'review-stuck-arbiter',
|
|
277
|
+
inlineContextSupplied: Boolean(args.inlineContext),
|
|
278
|
+
});
|
|
279
|
+
if (!args.inlineContext) {
|
|
280
|
+
throw new ReviewerRoundError(new NealProviderError({
|
|
281
|
+
message: 'review-stuck-arbiter round requires Neal-inlined reviewer context',
|
|
282
|
+
provider: args.reviewer.provider,
|
|
283
|
+
role: 'structured-advisor',
|
|
284
|
+
sessionHandle: null,
|
|
285
|
+
kind: 'provider_failed',
|
|
286
|
+
retryable: false,
|
|
287
|
+
}));
|
|
288
|
+
}
|
|
289
|
+
const schema = buildBlockedAdjudicatorSchema();
|
|
290
|
+
const { sessionHandle, structured } = await runReviewerStructuredRound({
|
|
291
|
+
reviewer: args.reviewer,
|
|
292
|
+
label: 'review-stuck-arbiter',
|
|
293
|
+
cwd: args.cwd,
|
|
294
|
+
prompt: buildBlockedAdjudicatorPrompt({
|
|
295
|
+
blockedReason: args.blockedReason,
|
|
296
|
+
inlineContext: args.inlineContext,
|
|
297
|
+
}),
|
|
298
|
+
schema,
|
|
299
|
+
structuredJsonProtocol: buildStructuredJsonProtocolSpec({
|
|
300
|
+
schemaLabel: 'blocked_adjudicator_payload',
|
|
301
|
+
schema,
|
|
302
|
+
validator: validateBlockedAdjudicatorVerdictPayload,
|
|
303
|
+
}),
|
|
304
|
+
logger: args.logger,
|
|
305
|
+
});
|
|
306
|
+
return {
|
|
307
|
+
sessionHandle,
|
|
308
|
+
verdict: validateBlockedAdjudicatorVerdictPayload(structured),
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
export async function runCoderFinalCompletionSummaryRound(args) {
|
|
312
|
+
try {
|
|
313
|
+
const advisor = getStructuredAdvisorAdapter(args.coder);
|
|
314
|
+
const schema = buildFinalCompletionSummarySchema();
|
|
315
|
+
const result = await runWithAgentTurnLiveness({
|
|
316
|
+
provider: args.coder.provider,
|
|
317
|
+
role: 'structured-advisor',
|
|
318
|
+
label: 'final-completion',
|
|
319
|
+
startupTimeoutMs: Math.min(getAgentTurnStartupTimeoutMs(args.cwd), getInactivityTimeoutMs(args.cwd)),
|
|
320
|
+
// Structured-advisor turns retry locally: they are non-writing by policy
|
|
321
|
+
// and do not resume sessions at this call site.
|
|
322
|
+
retryLimit: getAgentTurnRetryLimit(args.cwd),
|
|
323
|
+
logger: args.logger,
|
|
324
|
+
baseSink: createStructuredAdvisorProviderEventSink({
|
|
325
|
+
advisor: args.coder,
|
|
326
|
+
cwd: args.cwd,
|
|
327
|
+
label: 'final-completion',
|
|
328
|
+
logger: args.logger,
|
|
329
|
+
}),
|
|
330
|
+
run: (events, attempt) => advisor.runStructuredRound({
|
|
331
|
+
label: 'final-completion',
|
|
332
|
+
cwd: args.cwd,
|
|
333
|
+
prompt: buildFinalCompletionSummaryPrompt(args),
|
|
334
|
+
schema,
|
|
335
|
+
structuredJsonProtocol: buildStructuredJsonProtocolSpec({
|
|
336
|
+
schemaLabel: 'final_completion_summary_payload',
|
|
337
|
+
schema,
|
|
338
|
+
validator: parseFinalCompletionSummaryPayload,
|
|
339
|
+
}),
|
|
340
|
+
...getStructuredAdvisorRuntimeOptions(args.cwd),
|
|
341
|
+
signal: attempt.signal,
|
|
342
|
+
events,
|
|
343
|
+
}),
|
|
344
|
+
});
|
|
345
|
+
return {
|
|
346
|
+
sessionHandle: result.sessionHandle,
|
|
347
|
+
summary: parseFinalCompletionSummaryPayload(result.structured),
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
catch (error) {
|
|
351
|
+
throw translateCoderProviderError(error);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
export async function runReviewerFinalCompletionRound(args) {
|
|
355
|
+
assertReviewerRoundSupportsNoReadReviewer({
|
|
356
|
+
reviewer: args.reviewer,
|
|
357
|
+
label: 'final-completion',
|
|
358
|
+
inlineContextSupplied: Boolean(args.inlineContext),
|
|
359
|
+
});
|
|
360
|
+
const schema = buildFinalCompletionReviewerSchema();
|
|
361
|
+
const { sessionHandle, structured } = await runReviewerStructuredRound({
|
|
362
|
+
reviewer: args.reviewer,
|
|
363
|
+
label: 'final-completion',
|
|
364
|
+
cwd: args.cwd,
|
|
365
|
+
// The doctrine access mode comes from the reviewer provider's declared
|
|
366
|
+
// structured-advisor tool access, not from inline-context presence alone.
|
|
367
|
+
prompt: buildFinalCompletionReviewerPrompt({ ...args, accessMode: getReviewerDoctrineAccessMode(args.reviewer) }),
|
|
368
|
+
schema,
|
|
369
|
+
structuredJsonProtocol: buildStructuredJsonProtocolSpec({
|
|
370
|
+
schemaLabel: 'final_completion_reviewer_payload',
|
|
371
|
+
schema,
|
|
372
|
+
validator: parseFinalCompletionReviewerPayload,
|
|
373
|
+
}),
|
|
374
|
+
logger: args.logger,
|
|
375
|
+
});
|
|
376
|
+
let verdict;
|
|
377
|
+
try {
|
|
378
|
+
verdict = parseFinalCompletionReviewerPayload(structured);
|
|
379
|
+
}
|
|
380
|
+
catch (error) {
|
|
381
|
+
throw new FinalCompletionReviewerVerdictError({
|
|
382
|
+
sessionHandle,
|
|
383
|
+
cause: error,
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
return {
|
|
387
|
+
sessionHandle,
|
|
388
|
+
verdict,
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
export async function runCoderScopeRound(args) {
|
|
392
|
+
if (args.sessionHandle && args.coderSessionProtocol === null) {
|
|
393
|
+
throw new Error('Cannot resume coder scope session without coderSessionProtocol.');
|
|
394
|
+
}
|
|
395
|
+
if (!args.sessionHandle || args.coderSessionProtocol === 'structured_json_v1') {
|
|
396
|
+
const progressText = await safeReadText(args.progressMarkdownPath);
|
|
397
|
+
const schema = buildCoderScopeSchema();
|
|
398
|
+
const { sessionHandle, structured } = await runCoderStructuredPrompt({
|
|
399
|
+
coder: args.coder,
|
|
400
|
+
cwd: args.cwd,
|
|
401
|
+
prompt: buildScopePrompt(args.planDoc, progressText, { unattended: args.unattended }),
|
|
402
|
+
schema,
|
|
403
|
+
label: 'Coder scope round',
|
|
404
|
+
structuredJsonProtocol: buildStructuredJsonProtocolSpec({
|
|
405
|
+
schemaLabel: 'coder_scope_payload',
|
|
406
|
+
schema,
|
|
407
|
+
validator: validateCoderScopePayload,
|
|
408
|
+
}),
|
|
409
|
+
resumeHandle: args.sessionHandle,
|
|
410
|
+
onSessionStarted: args.onSessionStarted,
|
|
411
|
+
logger: args.logger,
|
|
412
|
+
});
|
|
413
|
+
const payload = validateCoderScopePayload(structured);
|
|
414
|
+
const marker = markerForCoderScopeAction(payload.action);
|
|
415
|
+
const responseWithoutProgressPayload = renderCoderScopeResponseWithoutProgressPayload(payload);
|
|
416
|
+
return {
|
|
417
|
+
sessionHandle,
|
|
418
|
+
finalResponse: renderCoderScopePayload(payload, marker),
|
|
419
|
+
responseWithoutProgressPayload,
|
|
420
|
+
marker,
|
|
421
|
+
progressJustification: payload.progress,
|
|
422
|
+
manualGate: payload.manualGate,
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
const coder = getCoderAdapter(args.coder);
|
|
426
|
+
const progressText = await safeReadText(args.progressMarkdownPath);
|
|
427
|
+
let finalResponse;
|
|
428
|
+
let sessionHandle;
|
|
429
|
+
try {
|
|
430
|
+
const result = await runWithAgentTurnLiveness({
|
|
431
|
+
provider: args.coder.provider,
|
|
432
|
+
role: 'coder',
|
|
433
|
+
startupTimeoutMs: Math.min(getAgentTurnStartupTimeoutMs(args.cwd), getInactivityTimeoutMs(args.cwd)),
|
|
434
|
+
// Resumed sessions never retry in the supervisor; the orchestrator's
|
|
435
|
+
// fresh-session retry owns recovery. This legacy
|
|
436
|
+
// branch always resumes, so the retry limit is always 0 here.
|
|
437
|
+
retryLimit: args.sessionHandle ? 0 : getAgentTurnRetryLimit(args.cwd),
|
|
438
|
+
logger: args.logger,
|
|
439
|
+
baseSink: createCoderProviderEventSink({
|
|
440
|
+
coder: args.coder,
|
|
441
|
+
cwd: args.cwd,
|
|
442
|
+
logger: args.logger,
|
|
443
|
+
}),
|
|
444
|
+
run: (events, attempt) => coder.runPrompt({
|
|
445
|
+
cwd: args.cwd,
|
|
446
|
+
prompt: buildLegacyScopePrompt(args.planDoc, progressText),
|
|
447
|
+
...getCoderRuntimeOptions(args.cwd),
|
|
448
|
+
resumeHandle: args.sessionHandle,
|
|
449
|
+
// Guarded so an abandoned attempt's late session handle can never
|
|
450
|
+
// overwrite a newer attempt's handle.
|
|
451
|
+
onSessionStarted: attempt.guard(args.onSessionStarted),
|
|
452
|
+
signal: attempt.signal,
|
|
453
|
+
events,
|
|
454
|
+
}),
|
|
455
|
+
});
|
|
456
|
+
finalResponse = result.finalResponse;
|
|
457
|
+
sessionHandle = result.sessionHandle;
|
|
458
|
+
}
|
|
459
|
+
catch (error) {
|
|
460
|
+
throw translateCoderProviderError(error);
|
|
461
|
+
}
|
|
462
|
+
const progressJustification = parseExecuteScopeProgressPayload(finalResponse);
|
|
463
|
+
const responseWithoutProgressPayload = stripExecuteScopeProgressPayload(finalResponse);
|
|
464
|
+
const marker = extractMarker(responseWithoutProgressPayload);
|
|
465
|
+
if (marker === AUTONOMY_SPLIT_PLAN) {
|
|
466
|
+
const derivedPlan = responseWithoutProgressPayload
|
|
467
|
+
.split(/\r?\n/)
|
|
468
|
+
.filter((line) => line.trim() !== AUTONOMY_SPLIT_PLAN)
|
|
469
|
+
.join('\n')
|
|
470
|
+
.trim();
|
|
471
|
+
if (!derivedPlan) {
|
|
472
|
+
throw new Error('Coder scope round returned AUTONOMY_SPLIT_PLAN without a derived plan body.');
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return {
|
|
476
|
+
sessionHandle,
|
|
477
|
+
finalResponse,
|
|
478
|
+
responseWithoutProgressPayload,
|
|
479
|
+
marker,
|
|
480
|
+
progressJustification,
|
|
481
|
+
manualGate: null,
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
function markerForCoderScopeAction(action) {
|
|
485
|
+
switch (action) {
|
|
486
|
+
case 'continue':
|
|
487
|
+
return AUTONOMY_CHUNK_DONE;
|
|
488
|
+
case 'scope_done':
|
|
489
|
+
return AUTONOMY_SCOPE_DONE;
|
|
490
|
+
case 'done':
|
|
491
|
+
return AUTONOMY_DONE;
|
|
492
|
+
case 'blocked':
|
|
493
|
+
return AUTONOMY_BLOCKED;
|
|
494
|
+
case 'split_plan':
|
|
495
|
+
return AUTONOMY_SPLIT_PLAN;
|
|
496
|
+
case 'manual_gate':
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
function renderCoderScopeResponseWithoutProgressPayload(payload) {
|
|
501
|
+
if (payload.action === 'split_plan') {
|
|
502
|
+
return payload.derivedPlan.trim();
|
|
503
|
+
}
|
|
504
|
+
if (payload.action === 'blocked') {
|
|
505
|
+
return (payload.blockedReason || payload.message).trim();
|
|
506
|
+
}
|
|
507
|
+
if (payload.action === 'manual_gate') {
|
|
508
|
+
return payload.message.trim();
|
|
509
|
+
}
|
|
510
|
+
return payload.message.trim();
|
|
511
|
+
}
|
|
512
|
+
function renderCoderScopePayload(payload, marker) {
|
|
513
|
+
return [renderCoderScopeResponseWithoutProgressPayload(payload), marker ?? '']
|
|
514
|
+
.map((part) => part.trim())
|
|
515
|
+
.filter((part) => part.length > 0)
|
|
516
|
+
.join('\n\n');
|
|
517
|
+
}
|
|
518
|
+
async function readPlanDocumentForPrompt(planDoc) {
|
|
519
|
+
try {
|
|
520
|
+
return await readFile(planDoc, 'utf8');
|
|
521
|
+
}
|
|
522
|
+
catch {
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
export async function runCoderPlanRound(args) {
|
|
527
|
+
if (args.sessionHandle && args.coderSessionProtocol === null) {
|
|
528
|
+
throw new Error('Cannot resume planner planning session without plannerSessionProtocol.');
|
|
529
|
+
}
|
|
530
|
+
if (!args.sessionHandle || args.coderSessionProtocol === 'structured_json_v1') {
|
|
531
|
+
const schema = buildCoderPlanSchema();
|
|
532
|
+
const planDocument = await readPlanDocumentForPrompt(args.planDoc);
|
|
533
|
+
const { sessionHandle, structured } = await runCoderStructuredPrompt({
|
|
534
|
+
coder: args.coder,
|
|
535
|
+
cwd: args.cwd,
|
|
536
|
+
prompt: buildPlanningPrompt(args.planDoc, planDocument, {
|
|
537
|
+
unattended: args.unattended,
|
|
538
|
+
authoredOneShot: args.authoredOneShot,
|
|
539
|
+
}),
|
|
540
|
+
schema,
|
|
541
|
+
label: 'Planner plan round',
|
|
542
|
+
structuredJsonProtocol: buildStructuredJsonProtocolSpec({
|
|
543
|
+
schemaLabel: 'coder_plan_payload',
|
|
544
|
+
schema,
|
|
545
|
+
validator: validateCoderPlanPayload,
|
|
546
|
+
}),
|
|
547
|
+
toolPolicy: { allowedWritePaths: [args.planDoc], allowRun: false },
|
|
548
|
+
resumeHandle: args.sessionHandle,
|
|
549
|
+
onSessionStarted: args.onSessionStarted,
|
|
550
|
+
telemetryLabel: 'planner',
|
|
551
|
+
logger: args.logger,
|
|
552
|
+
});
|
|
553
|
+
const payload = validateCoderPlanPayload(structured);
|
|
554
|
+
const marker = payload.action === 'blocked' ? AUTONOMY_BLOCKED : AUTONOMY_DONE;
|
|
555
|
+
const normalizedPayload = payload.action === 'ready_for_review'
|
|
556
|
+
? {
|
|
557
|
+
...payload,
|
|
558
|
+
planBody: normalizeExecutionShapeDeclaration(payload.planBody, payload.executionShape),
|
|
559
|
+
}
|
|
560
|
+
: payload;
|
|
561
|
+
if (normalizedPayload.action === 'ready_for_review') {
|
|
562
|
+
await writeFile(args.planDoc, normalizedPayload.planBody, 'utf8');
|
|
563
|
+
}
|
|
564
|
+
const finalResponse = renderCoderPlanPayload(normalizedPayload, marker);
|
|
565
|
+
return {
|
|
566
|
+
sessionHandle,
|
|
567
|
+
finalResponse,
|
|
568
|
+
marker,
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
const coder = getCoderAdapter(args.coder);
|
|
572
|
+
let finalResponse;
|
|
573
|
+
let sessionHandle;
|
|
574
|
+
const planDocument = await readPlanDocumentForPrompt(args.planDoc);
|
|
575
|
+
try {
|
|
576
|
+
const result = await runWithAgentTurnLiveness({
|
|
577
|
+
provider: args.coder.provider,
|
|
578
|
+
role: 'coder',
|
|
579
|
+
label: 'planner',
|
|
580
|
+
startupTimeoutMs: Math.min(getAgentTurnStartupTimeoutMs(args.cwd), getInactivityTimeoutMs(args.cwd)),
|
|
581
|
+
// Resumed sessions never retry in the supervisor; the orchestrator's
|
|
582
|
+
// fresh-session retry owns recovery. This legacy
|
|
583
|
+
// branch always resumes, so the retry limit is always 0 here.
|
|
584
|
+
retryLimit: args.sessionHandle ? 0 : getAgentTurnRetryLimit(args.cwd),
|
|
585
|
+
logger: args.logger,
|
|
586
|
+
baseSink: createCoderProviderEventSink({
|
|
587
|
+
coder: args.coder,
|
|
588
|
+
cwd: args.cwd,
|
|
589
|
+
label: 'planner',
|
|
590
|
+
logger: args.logger,
|
|
591
|
+
}),
|
|
592
|
+
run: (events, attempt) => coder.runPrompt({
|
|
593
|
+
cwd: args.cwd,
|
|
594
|
+
prompt: buildLegacyPlanningPrompt(args.planDoc, planDocument),
|
|
595
|
+
...getCoderRuntimeOptions(args.cwd),
|
|
596
|
+
toolPolicy: { allowedWritePaths: [args.planDoc], allowRun: false },
|
|
597
|
+
resumeHandle: args.sessionHandle,
|
|
598
|
+
// Guarded so an abandoned attempt's late session handle can never
|
|
599
|
+
// overwrite a newer attempt's handle.
|
|
600
|
+
onSessionStarted: attempt.guard(args.onSessionStarted),
|
|
601
|
+
signal: attempt.signal,
|
|
602
|
+
events,
|
|
603
|
+
}),
|
|
604
|
+
});
|
|
605
|
+
finalResponse = result.finalResponse;
|
|
606
|
+
sessionHandle = result.sessionHandle;
|
|
607
|
+
}
|
|
608
|
+
catch (error) {
|
|
609
|
+
throw translateCoderProviderError(error);
|
|
610
|
+
}
|
|
611
|
+
const marker = extractMarker(finalResponse);
|
|
612
|
+
return {
|
|
613
|
+
sessionHandle,
|
|
614
|
+
finalResponse,
|
|
615
|
+
marker,
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
function renderCoderPlanPayload(payload, marker) {
|
|
619
|
+
const parts = [payload.message, payload.planBody, payload.blockedReason]
|
|
620
|
+
.map((part) => part.trim())
|
|
621
|
+
.filter((part) => part.length > 0);
|
|
622
|
+
return [...parts, marker].join('\n\n');
|
|
623
|
+
}
|
|
624
|
+
export async function runCoderResponseRound(args) {
|
|
625
|
+
const progressText = await safeReadText(args.progressMarkdownPath);
|
|
626
|
+
const schema = buildCoderResponseSchema();
|
|
627
|
+
const { sessionHandle, structured } = await runCoderStructuredPrompt({
|
|
628
|
+
coder: args.coder,
|
|
629
|
+
cwd: args.cwd,
|
|
630
|
+
prompt: buildCoderResponsePrompt({
|
|
631
|
+
planDoc: args.planDoc,
|
|
632
|
+
progressText,
|
|
633
|
+
verificationHint: args.verificationHint,
|
|
634
|
+
openFindings: args.openFindings,
|
|
635
|
+
mode: args.mode,
|
|
636
|
+
}),
|
|
637
|
+
schema,
|
|
638
|
+
label: 'Coder response round',
|
|
639
|
+
structuredJsonProtocol: buildStructuredJsonProtocolSpec({
|
|
640
|
+
schemaLabel: 'coder_response_payload',
|
|
641
|
+
schema,
|
|
642
|
+
validator: validateCoderResponsePayload,
|
|
643
|
+
}),
|
|
644
|
+
resumeHandle: args.sessionHandle,
|
|
645
|
+
logger: args.logger,
|
|
646
|
+
});
|
|
647
|
+
return {
|
|
648
|
+
sessionHandle,
|
|
649
|
+
payload: validateCoderResponsePayload(structured),
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
export async function runBlockedRecoveryCoderRound(args) {
|
|
653
|
+
const progressText = await safeReadText(args.progressMarkdownPath);
|
|
654
|
+
const schema = buildCoderBlockedRecoveryDispositionSchema();
|
|
655
|
+
const { sessionHandle, structured } = await runCoderStructuredPrompt({
|
|
656
|
+
coder: args.coder,
|
|
657
|
+
cwd: args.cwd,
|
|
658
|
+
prompt: buildBlockedRecoveryCoderPrompt({
|
|
659
|
+
planDoc: args.planDoc,
|
|
660
|
+
progressText,
|
|
661
|
+
recoveryMarkdownPath: args.recoveryMarkdownPath,
|
|
662
|
+
blockedReason: args.blockedReason,
|
|
663
|
+
operatorGuidance: args.operatorGuidance,
|
|
664
|
+
maxTurns: args.maxTurns,
|
|
665
|
+
turnsTaken: args.turnsTaken,
|
|
666
|
+
terminalOnly: args.terminalOnly,
|
|
667
|
+
allowReplacement: args.allowReplacement,
|
|
668
|
+
}),
|
|
669
|
+
schema,
|
|
670
|
+
label: 'Coder blocked-recovery round',
|
|
671
|
+
structuredJsonProtocol: buildStructuredJsonProtocolSpec({
|
|
672
|
+
schemaLabel: 'coder_blocked_recovery_disposition_payload',
|
|
673
|
+
schema,
|
|
674
|
+
validator: validateCoderBlockedRecoveryDispositionPayload,
|
|
675
|
+
}),
|
|
676
|
+
resumeHandle: args.sessionHandle,
|
|
677
|
+
logger: args.logger,
|
|
678
|
+
});
|
|
679
|
+
return {
|
|
680
|
+
sessionHandle,
|
|
681
|
+
payload: validateCoderBlockedRecoveryDispositionPayload(structured),
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
export async function runCoderPlanResponseRound(args) {
|
|
685
|
+
const schema = buildCoderPlanResponseSchema();
|
|
686
|
+
const { sessionHandle, structured } = await runCoderStructuredPrompt({
|
|
687
|
+
coder: args.coder,
|
|
688
|
+
cwd: args.cwd,
|
|
689
|
+
prompt: buildCoderPlanResponsePrompt(args),
|
|
690
|
+
schema,
|
|
691
|
+
label: 'Planner plan-response round',
|
|
692
|
+
structuredJsonProtocol: buildStructuredJsonProtocolSpec({
|
|
693
|
+
schemaLabel: 'coder_plan_response_payload',
|
|
694
|
+
schema,
|
|
695
|
+
validator: validateCoderPlanResponsePayload,
|
|
696
|
+
}),
|
|
697
|
+
toolPolicy: { allowedWritePaths: [args.planDoc], allowRun: false },
|
|
698
|
+
resumeHandle: args.sessionHandle,
|
|
699
|
+
telemetryLabel: 'planner',
|
|
700
|
+
logger: args.logger,
|
|
701
|
+
});
|
|
702
|
+
return {
|
|
703
|
+
sessionHandle,
|
|
704
|
+
payload: validateCoderPlanResponsePayload(structured),
|
|
705
|
+
};
|
|
706
|
+
}
|