@navels/neal 0.1.0 → 0.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 +206 -209
- package/SECURITY.md +16 -19
- package/dist/neal/adjudicator/{blocked-adjudicator.js → consultant.js} +21 -21
- package/dist/neal/adjudicator/execute.js +33 -33
- package/dist/neal/adjudicator/final-completion.js +2 -31
- package/dist/neal/adjudicator/planning.js +264 -24
- package/dist/neal/agents/prompts.js +7 -15
- package/dist/neal/agents/rounds.js +51 -73
- package/dist/neal/agents/schemas.js +615 -502
- package/dist/neal/agents/structured-json.js +36 -0
- package/dist/neal/blocked-guidance.js +16 -9
- package/dist/neal/cli.js +2 -2
- package/dist/neal/commands/check.js +2 -2
- package/dist/neal/commands/compat.js +100 -36
- package/dist/neal/commands/interactive-activity.js +2 -2
- package/dist/neal/commands/recovery-guidance.js +20 -8
- package/dist/neal/commands/runtime.js +4 -2
- package/dist/neal/config.js +40 -7
- package/dist/neal/context/inline-review-context.js +13 -28
- package/dist/neal/context/reviewer-context.js +19 -14
- package/dist/neal/eval/reviewer-eval-manifest.js +86 -0
- package/dist/neal/eval/reviewer-eval.js +155 -0
- package/dist/neal/git.js +9 -3
- package/dist/neal/interactive-controls.js +5 -0
- package/dist/neal/orchestrator/completion.js +215 -113
- package/dist/neal/orchestrator/failures.js +7 -0
- package/dist/neal/orchestrator/notifications.js +9 -9
- package/dist/neal/orchestrator/phases/planning.js +124 -112
- package/dist/neal/orchestrator/phases/recovery.js +151 -115
- package/dist/neal/orchestrator/phases/review.js +28 -1
- package/dist/neal/orchestrator/split-plan.js +12 -11
- package/dist/neal/orchestrator/transitions.js +36 -71
- package/dist/neal/orchestrator.js +3 -0
- package/dist/neal/plan-doc.js +24 -1
- package/dist/neal/plan-queue.js +29 -1
- package/dist/neal/prompts/assert-builder.js +8 -1
- package/dist/neal/prompts/execute.js +11 -19
- package/dist/neal/prompts/planning.js +64 -55
- package/dist/neal/prompts/review-doctrine.js +13 -50
- package/dist/neal/prompts/specialized.js +28 -22
- package/dist/neal/prompts/specs.js +223 -17
- package/dist/neal/providers/agent-settings-isolation.js +35 -0
- package/dist/neal/providers/anthropic-claude.js +309 -248
- package/dist/neal/providers/detection.js +3 -7
- package/dist/neal/providers/git-config-isolation.js +83 -0
- package/dist/neal/providers/openai-codex.js +102 -204
- package/dist/neal/providers/{generic-agentic-tools.js → openai-compatible-tools.js} +11 -5
- package/dist/neal/providers/openai-compatible.js +1079 -403
- package/dist/neal/providers/openrouter-routing.js +68 -0
- package/dist/neal/providers/pricing.js +124 -0
- package/dist/neal/providers/rate-card.js +2275 -0
- package/dist/neal/providers/registry.js +6 -17
- package/dist/neal/providers/telemetry.js +4 -0
- package/dist/neal/providers/types.js +12 -0
- package/dist/neal/resume-decision.js +7 -6
- package/dist/neal/resume-planner.js +9 -0
- package/dist/neal/retrospective.js +36 -7
- package/dist/neal/review-debt.js +30 -0
- package/dist/neal/review-findings/provider.js +17 -29
- package/dist/neal/review.js +17 -1
- package/dist/neal/run-metrics.js +74 -9
- package/dist/neal/state-invariants.js +11 -3
- package/dist/neal/state-views.js +27 -5
- package/dist/neal/state.js +59 -15
- package/dist/neal/status.js +28 -1
- package/dist/neal/support.js +3 -3
- package/docs/README.md +22 -18
- package/docs/{ADJUDICATOR_INVENTORY.md → adjudicator-inventory.md} +58 -57
- package/docs/architecture.md +79 -83
- package/docs/automation.md +11 -11
- package/docs/compat.md +91 -61
- package/docs/compatible-models.md +145 -110
- package/docs/demo.md +15 -5
- package/docs/maintenance.md +74 -32
- package/docs/plan-format.md +61 -33
- package/docs/prompt-evals.md +96 -0
- package/docs/{PROMPT_SPECS.md → prompt-specs.md} +52 -48
- package/docs/providers.md +330 -283
- package/docs/release.md +43 -40
- package/docs/review-convergence.md +246 -0
- package/docs/state-machine.md +61 -59
- package/docs/storage.md +40 -31
- package/docs/troubleshooting.md +32 -29
- package/examples/compat/add-edit-verify/PLAN.md +2 -0
- package/examples/compat/add-edit-verify/broken.diff +2 -2
- package/examples/compat/add-edit-verify/good.diff +2 -2
- package/examples/compat/add-edit-verify/src/add.js +1 -1
- package/examples/compat/manifest.json +25 -29
- package/examples/compat/sum-grep-edit/PLAN.md +33 -0
- package/examples/compat/sum-grep-edit/broken.diff +12 -0
- package/examples/compat/sum-grep-edit/good.diff +12 -0
- package/examples/compat/sum-grep-edit/package.json +5 -0
- package/examples/compat/sum-grep-edit/src/numbers.js +9 -0
- package/examples/compat/sum-grep-edit/test/numbers.test.js +15 -0
- package/examples/issue-triage-js/README.md +0 -15
- package/examples/reviewer-eval/clean-doc/base/src/clamp.ts +3 -0
- package/examples/reviewer-eval/clean-doc/change.diff +9 -0
- package/examples/reviewer-eval/clean-extract/base/src/greet.ts +3 -0
- package/examples/reviewer-eval/clean-extract/change.diff +13 -0
- package/examples/reviewer-eval/dropped-error/base/src/load.ts +4 -0
- package/examples/reviewer-eval/dropped-error/change.diff +15 -0
- package/examples/reviewer-eval/manifest.json +45 -0
- package/examples/reviewer-eval/off-by-one/base/src/paginate.ts +5 -0
- package/examples/reviewer-eval/off-by-one/change.diff +11 -0
- package/{config.yml → neal.yml} +1 -1
- package/package.json +20 -15
- package/dist/neal/providers/generic-agentic.js +0 -1238
- package/docs/comparison.md +0 -105
- package/examples/compat/is-even-add-test/PLAN.md +0 -30
- package/examples/compat/is-even-add-test/broken.diff +0 -11
- package/examples/compat/is-even-add-test/good.diff +0 -11
- package/examples/compat/is-even-add-test/package.json +0 -5
- package/examples/compat/is-even-add-test/src/is-even.js +0 -3
- package/examples/compat/is-even-add-test/test/is-even.test.js +0 -9
- package/examples/compat/reverse-grep-edit/PLAN.md +0 -32
- package/examples/compat/reverse-grep-edit/broken.diff +0 -12
- package/examples/compat/reverse-grep-edit/good.diff +0 -12
- package/examples/compat/reverse-grep-edit/package.json +0 -5
- package/examples/compat/reverse-grep-edit/src/strings.js +0 -10
- package/examples/compat/reverse-grep-edit/test/strings.test.js +0 -15
|
@@ -2,6 +2,8 @@ import { createRequire } from 'node:module';
|
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
3
|
import { query, } from '@anthropic-ai/claude-agent-sdk';
|
|
4
4
|
import { buildStructuredJsonPrompt, runStructuredJsonProtocol, runStructuredJsonRepairLoop, validateStructuredJsonPayload, } from '../agents/structured-json.js';
|
|
5
|
+
import { agentSettingsIsolated } from './agent-settings-isolation.js';
|
|
6
|
+
import { agentSubprocessEnv } from './git-config-isolation.js';
|
|
5
7
|
import { NealProviderError } from './types.js';
|
|
6
8
|
const ANTHROPIC_CLAUDE_PROVIDER_ID = 'anthropic-claude';
|
|
7
9
|
const requireFromProvider = createRequire(import.meta.url);
|
|
@@ -57,7 +59,7 @@ class ClaudeInactivityTimeoutError extends Error {
|
|
|
57
59
|
}
|
|
58
60
|
// In-round transient (`api_error`) retries back off exponentially: 500 ms
|
|
59
61
|
// base, doubling per retry, capped at 5 s — the same schedule as the
|
|
60
|
-
//
|
|
62
|
+
// openai-compatible adapter. `sleep` is injectable through the run functions so
|
|
61
63
|
// tests observe the delays without wall-clock waiting.
|
|
62
64
|
const API_RETRY_BASE_DELAY_MS = 500;
|
|
63
65
|
const API_RETRY_MAX_DELAY_MS = 5_000;
|
|
@@ -125,9 +127,44 @@ async function appendClaudeText(label, state, text, events, sessionHandle, role
|
|
|
125
127
|
}
|
|
126
128
|
return emittedTexts;
|
|
127
129
|
}
|
|
128
|
-
|
|
130
|
+
function getClaudeBashCommand(toolInput) {
|
|
131
|
+
if (toolInput === null || typeof toolInput !== 'object') {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
const command = toolInput.command;
|
|
135
|
+
return typeof command === 'string' && command.trim() ? command : null;
|
|
136
|
+
}
|
|
137
|
+
// Extracts the human-readable text of a tool_result block: string content is
|
|
138
|
+
// used as-is; block-array content contributes its text blocks. Non-text
|
|
139
|
+
// content (images, documents) yields null.
|
|
140
|
+
function getClaudeToolResultText(content) {
|
|
141
|
+
if (typeof content === 'string') {
|
|
142
|
+
return content;
|
|
143
|
+
}
|
|
144
|
+
if (!Array.isArray(content)) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
const texts = [];
|
|
148
|
+
for (const block of content) {
|
|
149
|
+
if (block !== null &&
|
|
150
|
+
typeof block === 'object' &&
|
|
151
|
+
block.type === 'text' &&
|
|
152
|
+
typeof block.text === 'string') {
|
|
153
|
+
texts.push(block.text);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return texts.length > 0 ? texts.join('\n') : null;
|
|
157
|
+
}
|
|
158
|
+
async function logClaudeMessage(label, message, events, state, sessionHandle, role = roleForClaudeLabel(label), cwd) {
|
|
129
159
|
switch (message.type) {
|
|
130
160
|
case 'assistant': {
|
|
161
|
+
if (state) {
|
|
162
|
+
for (const block of message.message.content) {
|
|
163
|
+
if (block.type === 'tool_use') {
|
|
164
|
+
state.pendingToolUses.set(block.id, { name: block.name, input: block.input });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
131
168
|
if (state?.sawTextDelta) {
|
|
132
169
|
return [];
|
|
133
170
|
}
|
|
@@ -150,6 +187,72 @@ async function logClaudeMessage(label, message, events, state, sessionHandle, ro
|
|
|
150
187
|
}
|
|
151
188
|
break;
|
|
152
189
|
}
|
|
190
|
+
case 'user': {
|
|
191
|
+
// SDK tool executions surface as user messages carrying tool_result
|
|
192
|
+
// blocks. Matching them against the turn's pending tool_use blocks maps
|
|
193
|
+
// Bash runs to `command_completed` and successful write-tool calls to
|
|
194
|
+
// `file_changed`. Read-class tools (Read/Grep/Glob) intentionally emit
|
|
195
|
+
// nothing here — they are not verification evidence. The mapping is
|
|
196
|
+
// role-agnostic: advisor rounds run a read-only toolset, so they never
|
|
197
|
+
// produce Bash or write tool_uses.
|
|
198
|
+
if (!state) {
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
const content = message.message.content;
|
|
202
|
+
if (!Array.isArray(content)) {
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
for (const block of content) {
|
|
206
|
+
if (block.type !== 'tool_result') {
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
const pending = state.pendingToolUses.get(block.tool_use_id);
|
|
210
|
+
if (!pending) {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
state.pendingToolUses.delete(block.tool_use_id);
|
|
214
|
+
if (pending.name === 'Bash') {
|
|
215
|
+
const command = getClaudeBashCommand(pending.input);
|
|
216
|
+
if (!command) {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
// The SDK's tool_result exposes no exit code, so `exitCode` is
|
|
220
|
+
// omitted; `status` carries the is_error signal instead.
|
|
221
|
+
const output = getClaudeToolResultText(block.content);
|
|
222
|
+
await emitProviderEvent(events, {
|
|
223
|
+
type: 'command_completed',
|
|
224
|
+
provider: ANTHROPIC_CLAUDE_PROVIDER_ID,
|
|
225
|
+
role,
|
|
226
|
+
label,
|
|
227
|
+
sessionHandle,
|
|
228
|
+
itemId: block.tool_use_id,
|
|
229
|
+
command,
|
|
230
|
+
status: block.is_error === true ? 'failed' : 'completed',
|
|
231
|
+
...(output !== null ? { output } : {}),
|
|
232
|
+
...(cwd ? { cwd } : {}),
|
|
233
|
+
providerData: { sdkMessageType: message.type, toolName: pending.name, toolUseId: block.tool_use_id },
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
else if (CLAUDE_WRITE_TOOL_NAMES.has(pending.name) && block.is_error !== true) {
|
|
237
|
+
// An is_error result means the write did not happen (e.g. a
|
|
238
|
+
// write-path guard deny), so it must not claim a file change.
|
|
239
|
+
const filePath = getClaudeWriteToolPath(pending.input);
|
|
240
|
+
if (!filePath) {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
await emitProviderEvent(events, {
|
|
244
|
+
type: 'file_changed',
|
|
245
|
+
provider: ANTHROPIC_CLAUDE_PROVIDER_ID,
|
|
246
|
+
role,
|
|
247
|
+
label,
|
|
248
|
+
sessionHandle,
|
|
249
|
+
files: [filePath],
|
|
250
|
+
providerData: { sdkMessageType: message.type, toolName: pending.name, toolUseId: block.tool_use_id },
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
153
256
|
case 'stream_event':
|
|
154
257
|
if (message.event.type === 'content_block_delta') {
|
|
155
258
|
if (message.event.delta.type === 'text_delta' && state) {
|
|
@@ -274,6 +377,9 @@ async function logClaudeMessage(label, message, events, state, sessionHandle, ro
|
|
|
274
377
|
}
|
|
275
378
|
break;
|
|
276
379
|
case 'result':
|
|
380
|
+
// Turn boundary: a tool_use whose result never arrived must not match a
|
|
381
|
+
// tool_result from a later turn in the same stream.
|
|
382
|
+
state?.pendingToolUses.clear();
|
|
277
383
|
await emitProviderEvent(events, {
|
|
278
384
|
type: 'turn_completed',
|
|
279
385
|
provider: ANTHROPIC_CLAUDE_PROVIDER_ID,
|
|
@@ -281,6 +387,10 @@ async function logClaudeMessage(label, message, events, state, sessionHandle, ro
|
|
|
281
387
|
label,
|
|
282
388
|
sessionHandle,
|
|
283
389
|
usage: message.usage,
|
|
390
|
+
// One `result` message is one query invocation = one turn, so
|
|
391
|
+
// `total_cost_usd` is a per-turn figure that sums cleanly across turns.
|
|
392
|
+
costUsd: message.total_cost_usd,
|
|
393
|
+
costSource: 'provider',
|
|
284
394
|
providerData: { sdkMessageType: message.type, subtype: message.subtype },
|
|
285
395
|
});
|
|
286
396
|
if (message.usage !== undefined) {
|
|
@@ -291,6 +401,8 @@ async function logClaudeMessage(label, message, events, state, sessionHandle, ro
|
|
|
291
401
|
label,
|
|
292
402
|
sessionHandle,
|
|
293
403
|
usage: message.usage,
|
|
404
|
+
costUsd: message.total_cost_usd,
|
|
405
|
+
costSource: 'provider',
|
|
294
406
|
providerData: { sdkMessageType: message.type, subtype: message.subtype },
|
|
295
407
|
});
|
|
296
408
|
}
|
|
@@ -401,7 +513,12 @@ async function collectClaudeResult(stream, cwd, label, inactivityTimeoutMs, even
|
|
|
401
513
|
let sessionHandle = null;
|
|
402
514
|
let lastResult = null;
|
|
403
515
|
let firstStructuredResult = null;
|
|
404
|
-
const logState = {
|
|
516
|
+
const logState = {
|
|
517
|
+
textBuffer: '',
|
|
518
|
+
sawTextDelta: false,
|
|
519
|
+
lastThinkingEmitMs: 0,
|
|
520
|
+
pendingToolUses: new Map(),
|
|
521
|
+
};
|
|
405
522
|
const assistantTexts = [];
|
|
406
523
|
const iterator = stream[Symbol.asyncIterator]();
|
|
407
524
|
while (true) {
|
|
@@ -435,7 +552,7 @@ async function collectClaudeResult(stream, cwd, label, inactivityTimeoutMs, even
|
|
|
435
552
|
providerData: { sdkMessageType: message.type },
|
|
436
553
|
});
|
|
437
554
|
}
|
|
438
|
-
const emittedAssistantTexts = await logClaudeMessage(label, message, events, logState, sessionHandle, role);
|
|
555
|
+
const emittedAssistantTexts = await logClaudeMessage(label, message, events, logState, sessionHandle, role, cwd);
|
|
439
556
|
if (emittedAssistantTexts.length > 0) {
|
|
440
557
|
assistantTexts.push(emittedAssistantTexts.join('\n\n'));
|
|
441
558
|
}
|
|
@@ -484,36 +601,72 @@ function createClaudeTurnAbortController(signal) {
|
|
|
484
601
|
function deriveClaudeAbortController(signal) {
|
|
485
602
|
return signal ? createClaudeTurnAbortController(signal) : undefined;
|
|
486
603
|
}
|
|
487
|
-
function
|
|
604
|
+
function buildClaudeCoreQueryOptions(spec) {
|
|
488
605
|
return {
|
|
489
|
-
cwd:
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
606
|
+
cwd: spec.cwd,
|
|
607
|
+
// Full environment plus the git-config isolation overrides: Claude's Bash
|
|
608
|
+
// tool must not be able to write the operator's real global gitconfig.
|
|
609
|
+
// See git-config-isolation.ts.
|
|
610
|
+
env: agentSubprocessEnv(),
|
|
611
|
+
...(spec.abortController ? { abortController: spec.abortController } : {}),
|
|
612
|
+
...(spec.model ? { model: spec.model } : {}),
|
|
613
|
+
...(spec.effort ? { effort: spec.effort } : {}),
|
|
614
|
+
tools: spec.tools,
|
|
615
|
+
...(spec.hooks ? { hooks: spec.hooks } : {}),
|
|
616
|
+
// Under compat qualification only, run Claude in the SDK's isolation mode
|
|
617
|
+
// (load no filesystem settings). Left unset, the SDK loads ~/.claude and any
|
|
618
|
+
// repo .claude by default, pulling in the operator's interactive config —
|
|
619
|
+
// permissions, statusline, CLAUDE.md, and the Stop / Notification hooks that
|
|
620
|
+
// fire a desktop notifier on every turn. compat runs the whole role matrix,
|
|
621
|
+
// so those hooks would spam one notification per turn; isolating keeps the
|
|
622
|
+
// capability probe quiet and hermetic. Normal neal runs (pipeline, local
|
|
623
|
+
// `neal go`) honor the operator's config — it's their machine. neal's own
|
|
624
|
+
// hooks (the coder write-path guard passed programmatically above) are
|
|
625
|
+
// unaffected either way. See agent-settings-isolation.ts.
|
|
626
|
+
...(agentSettingsIsolated() ? { settingSources: [] } : {}),
|
|
494
627
|
permissionMode: 'bypassPermissions',
|
|
495
628
|
allowDangerouslySkipPermissions: true,
|
|
496
|
-
...(
|
|
497
|
-
...(claudeExecutablePath ? { pathToClaudeCodeExecutable: claudeExecutablePath } : {}),
|
|
629
|
+
...(spec.resumeHandle ? { resume: spec.resumeHandle } : {}),
|
|
630
|
+
...(spec.claudeExecutablePath ? { pathToClaudeCodeExecutable: spec.claudeExecutablePath } : {}),
|
|
631
|
+
...(spec.outputSchema
|
|
632
|
+
? {
|
|
633
|
+
outputFormat: {
|
|
634
|
+
type: 'json_schema',
|
|
635
|
+
schema: spec.outputSchema,
|
|
636
|
+
},
|
|
637
|
+
}
|
|
638
|
+
: {}),
|
|
498
639
|
stderr: (data) => {
|
|
499
|
-
void
|
|
640
|
+
void spec.events?.({
|
|
500
641
|
type: 'tool_progress',
|
|
501
642
|
provider: ANTHROPIC_CLAUDE_PROVIDER_ID,
|
|
502
|
-
role:
|
|
503
|
-
label:
|
|
504
|
-
sessionHandle:
|
|
643
|
+
role: spec.stderrRole,
|
|
644
|
+
label: spec.stderrLabel,
|
|
645
|
+
sessionHandle: spec.stderrSessionHandle,
|
|
505
646
|
toolName: 'stderr',
|
|
506
647
|
message: data,
|
|
507
648
|
isError: true,
|
|
508
649
|
providerData: { stream: 'stderr' },
|
|
509
650
|
});
|
|
510
651
|
},
|
|
511
|
-
outputFormat: {
|
|
512
|
-
type: 'json_schema',
|
|
513
|
-
schema: args.schema,
|
|
514
|
-
},
|
|
515
652
|
};
|
|
516
653
|
}
|
|
654
|
+
function buildClaudeQueryOptions(args, defaultModel, claudeExecutablePath = getClaudeCodeExecutablePath(), defaultEffort, abortController = deriveClaudeAbortController(args.signal)) {
|
|
655
|
+
return buildClaudeCoreQueryOptions({
|
|
656
|
+
cwd: args.cwd,
|
|
657
|
+
abortController,
|
|
658
|
+
model: args.model ?? defaultModel,
|
|
659
|
+
effort: defaultEffort,
|
|
660
|
+
tools: ['Read', 'Grep', 'Glob'],
|
|
661
|
+
resumeHandle: args.resumeHandle,
|
|
662
|
+
claudeExecutablePath,
|
|
663
|
+
outputSchema: args.schema,
|
|
664
|
+
events: args.events,
|
|
665
|
+
stderrRole: 'structured-advisor',
|
|
666
|
+
stderrLabel: args.label,
|
|
667
|
+
stderrSessionHandle: args.resumeHandle ?? null,
|
|
668
|
+
});
|
|
669
|
+
}
|
|
517
670
|
function buildClaudeQueryStream(args, defaultModel, defaultEffort, abortController) {
|
|
518
671
|
return query({
|
|
519
672
|
prompt: args.prompt,
|
|
@@ -527,30 +680,19 @@ function buildClaudeJsonBlockReviewPrompt(args) {
|
|
|
527
680
|
return args.prompt;
|
|
528
681
|
}
|
|
529
682
|
function buildClaudeJsonBlockQueryOptions(args, defaultModel, claudeExecutablePath = getClaudeCodeExecutablePath(), defaultEffort, abortController = deriveClaudeAbortController(args.signal)) {
|
|
530
|
-
return {
|
|
683
|
+
return buildClaudeCoreQueryOptions({
|
|
531
684
|
cwd: args.cwd,
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
685
|
+
abortController,
|
|
686
|
+
model: args.model ?? defaultModel,
|
|
687
|
+
effort: defaultEffort,
|
|
535
688
|
tools: ['Read', 'Grep', 'Glob'],
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
provider: ANTHROPIC_CLAUDE_PROVIDER_ID,
|
|
544
|
-
role: 'structured-advisor',
|
|
545
|
-
label: args.label,
|
|
546
|
-
sessionHandle: args.resumeHandle ?? null,
|
|
547
|
-
toolName: 'stderr',
|
|
548
|
-
message: data,
|
|
549
|
-
isError: true,
|
|
550
|
-
providerData: { stream: 'stderr' },
|
|
551
|
-
});
|
|
552
|
-
},
|
|
553
|
-
};
|
|
689
|
+
resumeHandle: args.resumeHandle,
|
|
690
|
+
claudeExecutablePath,
|
|
691
|
+
events: args.events,
|
|
692
|
+
stderrRole: 'structured-advisor',
|
|
693
|
+
stderrLabel: args.label,
|
|
694
|
+
stderrSessionHandle: args.resumeHandle ?? null,
|
|
695
|
+
});
|
|
554
696
|
}
|
|
555
697
|
function buildClaudeJsonBlockQueryStream(args, defaultModel, defaultEffort, abortController) {
|
|
556
698
|
return query({
|
|
@@ -559,29 +701,18 @@ function buildClaudeJsonBlockQueryStream(args, defaultModel, defaultEffort, abor
|
|
|
559
701
|
});
|
|
560
702
|
}
|
|
561
703
|
function buildClaudeJsonBlockRepairQueryOptions(args, defaultModel, claudeExecutablePath = getClaudeCodeExecutablePath(), defaultEffort, abortController) {
|
|
562
|
-
return {
|
|
704
|
+
return buildClaudeCoreQueryOptions({
|
|
563
705
|
cwd: args.cwd,
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
706
|
+
abortController,
|
|
707
|
+
model: args.model ?? defaultModel,
|
|
708
|
+
effort: defaultEffort,
|
|
567
709
|
tools: [],
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
provider: ANTHROPIC_CLAUDE_PROVIDER_ID,
|
|
575
|
-
role: 'structured-advisor',
|
|
576
|
-
label: `${args.label}:structured-json-repair`,
|
|
577
|
-
sessionHandle: null,
|
|
578
|
-
toolName: 'stderr',
|
|
579
|
-
message: data,
|
|
580
|
-
isError: true,
|
|
581
|
-
providerData: { stream: 'stderr' },
|
|
582
|
-
});
|
|
583
|
-
},
|
|
584
|
-
};
|
|
710
|
+
claudeExecutablePath,
|
|
711
|
+
events: args.events,
|
|
712
|
+
stderrRole: 'structured-advisor',
|
|
713
|
+
stderrLabel: `${args.label}:structured-json-repair`,
|
|
714
|
+
stderrSessionHandle: null,
|
|
715
|
+
});
|
|
585
716
|
}
|
|
586
717
|
function buildClaudeJsonBlockRepairQueryStream(args, repairPrompt, defaultModel, defaultEffort, abortController) {
|
|
587
718
|
return query({
|
|
@@ -959,7 +1090,33 @@ async function validateOrRepairClaudeJsonBlockResponse(args) {
|
|
|
959
1090
|
throw providerError;
|
|
960
1091
|
}
|
|
961
1092
|
}
|
|
962
|
-
|
|
1093
|
+
// Both advisor protocols give up on an unsuccessful Claude result with the
|
|
1094
|
+
// same normalized provider error; only the loop position at which each
|
|
1095
|
+
// protocol reaches this differs (see runClaudeStructuredAdvisorRoundLoop).
|
|
1096
|
+
async function createClaudeAdvisorUnsuccessfulResultError(details) {
|
|
1097
|
+
const providerError = createClaudeProviderError({
|
|
1098
|
+
message: details.resultErrorMessage
|
|
1099
|
+
? `Claude ${details.label} did not return a successful result${details.subtype ? ` (${details.subtype})` : ''}: ${details.resultErrorMessage}`
|
|
1100
|
+
: `Claude ${details.label} did not return a successful result${details.subtype ? ` (${details.subtype})` : ''}`,
|
|
1101
|
+
role: 'structured-advisor',
|
|
1102
|
+
sessionHandle: details.sessionHandle,
|
|
1103
|
+
kind: getClaudeStructuredFailureKind(details.subtype, details.resultErrorMessage),
|
|
1104
|
+
subtype: details.subtype,
|
|
1105
|
+
});
|
|
1106
|
+
await emitClaudeProviderError(details.events, providerError, details.label);
|
|
1107
|
+
return providerError;
|
|
1108
|
+
}
|
|
1109
|
+
// One advisor round loop serves both structured-output protocols. The shared
|
|
1110
|
+
// skeleton — per-turn abort controller, assistant-text mirroring, transient
|
|
1111
|
+
// api_error retries with exponential backoff, transient error-result
|
|
1112
|
+
// retries, and the final unsuccessful-result error — is identical for both;
|
|
1113
|
+
// the `run.protocol` discriminator selects the json-block behavior (local
|
|
1114
|
+
// extraction/validation with the repair sub-loop, plus the mirrored-prose
|
|
1115
|
+
// recovery when the stream fails after emitting assistant text) or the
|
|
1116
|
+
// native behavior (SDK `json_schema` structured output with stream-error and
|
|
1117
|
+
// retry-exhaustion telemetry).
|
|
1118
|
+
async function runClaudeStructuredAdvisorRoundLoop(run, defaultModel, defaultEffort, sleep = defaultSleep) {
|
|
1119
|
+
const args = run.args;
|
|
963
1120
|
let sessionHandle = args.resumeHandle ?? null;
|
|
964
1121
|
let apiRetryCount = 0;
|
|
965
1122
|
const apiRetryLimit = args.apiRetryLimit;
|
|
@@ -974,56 +1131,9 @@ async function runClaudeJsonBlockStructuredAdvisorRound(args, defaultModel, crea
|
|
|
974
1131
|
await args.events?.(event);
|
|
975
1132
|
}
|
|
976
1133
|
: undefined;
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
result = await collectClaudeResult(createStream(args, defaultModel, defaultEffort, turnAbortController), args.cwd, args.label, args.inactivityTimeoutMs, primaryEvents, 'structured-advisor', turnAbortController);
|
|
981
|
-
}
|
|
982
|
-
catch (error) {
|
|
983
|
-
const providerError = normalizeClaudeProviderError(error, {
|
|
984
|
-
role: 'structured-advisor',
|
|
985
|
-
sessionHandle,
|
|
986
|
-
});
|
|
987
|
-
if (providerError.kind === 'api_error' && apiRetryCount < apiRetryLimit) {
|
|
988
|
-
apiRetryCount += 1;
|
|
989
|
-
await emitProviderEvent(args.events, {
|
|
990
|
-
type: 'tool_progress',
|
|
991
|
-
provider: ANTHROPIC_CLAUDE_PROVIDER_ID,
|
|
992
|
-
role: 'structured-advisor',
|
|
993
|
-
label: args.label,
|
|
994
|
-
sessionHandle,
|
|
995
|
-
toolName: 'api_retry',
|
|
996
|
-
message: `transient API failure; retrying review (${apiRetryCount}/${apiRetryLimit})`,
|
|
997
|
-
isError: true,
|
|
998
|
-
providerData: {
|
|
999
|
-
retryCount: apiRetryCount,
|
|
1000
|
-
retryLimit: apiRetryLimit,
|
|
1001
|
-
message: providerError.message,
|
|
1002
|
-
},
|
|
1003
|
-
});
|
|
1004
|
-
await sleep(getApiRetryDelayMs(apiRetryCount));
|
|
1005
|
-
continue;
|
|
1006
|
-
}
|
|
1007
|
-
const mirroredAssistantText = mirroredAssistantTexts.join('\n\n').trim();
|
|
1008
|
-
if (mirroredAssistantText &&
|
|
1009
|
-
(providerError.kind === 'structured_output_invalid' || providerError.kind === 'structured_output_missing')) {
|
|
1010
|
-
return await validateOrRepairClaudeJsonBlockResponse({
|
|
1011
|
-
roundArgs: args,
|
|
1012
|
-
assistantText: mirroredAssistantText,
|
|
1013
|
-
defaultModel,
|
|
1014
|
-
defaultEffort,
|
|
1015
|
-
sessionHandle: providerError.sessionHandle ?? sessionHandle,
|
|
1016
|
-
createRepairStream,
|
|
1017
|
-
});
|
|
1018
|
-
}
|
|
1019
|
-
await emitClaudeProviderError(args.events, providerError, args.label);
|
|
1020
|
-
throw providerError;
|
|
1021
|
-
}
|
|
1022
|
-
sessionHandle = result.sessionHandle ?? sessionHandle;
|
|
1023
|
-
const subtype = result.lastResult?.subtype ?? null;
|
|
1024
|
-
const resultErrorMessage = getClaudeResultErrorMessage(result.lastResult);
|
|
1025
|
-
const isErrorResult = result.lastResult?.is_error === true;
|
|
1026
|
-
if (isErrorResult && isTransientClaudeFailure(subtype, resultErrorMessage) && apiRetryCount < apiRetryLimit) {
|
|
1134
|
+
// Reads `sessionHandle` at call time, so post-collect callers observe the
|
|
1135
|
+
// handle updated from the just-collected result.
|
|
1136
|
+
const retryAfterTransientErrorResult = async (subtype, message) => {
|
|
1027
1137
|
apiRetryCount += 1;
|
|
1028
1138
|
await emitProviderEvent(args.events, {
|
|
1029
1139
|
type: 'tool_progress',
|
|
@@ -1038,60 +1148,15 @@ async function runClaudeJsonBlockStructuredAdvisorRound(args, defaultModel, crea
|
|
|
1038
1148
|
retryCount: apiRetryCount,
|
|
1039
1149
|
retryLimit: apiRetryLimit,
|
|
1040
1150
|
subtype,
|
|
1041
|
-
message
|
|
1151
|
+
message,
|
|
1042
1152
|
},
|
|
1043
1153
|
});
|
|
1044
1154
|
await sleep(getApiRetryDelayMs(apiRetryCount));
|
|
1045
|
-
|
|
1046
|
-
}
|
|
1047
|
-
const structuredRetryExhausted = isStructuredOutputRetryExhaustion(subtype, resultErrorMessage);
|
|
1048
|
-
if (subtype && subtype !== 'success' && !structuredRetryExhausted) {
|
|
1049
|
-
const providerError = createClaudeProviderError({
|
|
1050
|
-
message: resultErrorMessage
|
|
1051
|
-
? `Claude ${args.label} did not return a successful result${subtype ? ` (${subtype})` : ''}: ${resultErrorMessage}`
|
|
1052
|
-
: `Claude ${args.label} did not return a successful result${subtype ? ` (${subtype})` : ''}`,
|
|
1053
|
-
role: 'structured-advisor',
|
|
1054
|
-
sessionHandle,
|
|
1055
|
-
kind: getClaudeStructuredFailureKind(subtype, resultErrorMessage),
|
|
1056
|
-
subtype,
|
|
1057
|
-
});
|
|
1058
|
-
await emitClaudeProviderError(args.events, providerError, args.label);
|
|
1059
|
-
throw providerError;
|
|
1060
|
-
}
|
|
1061
|
-
const assistantText = getClaudeAssistantTextForLocalJson(result, mirroredAssistantTexts.join('\n\n').trim());
|
|
1062
|
-
return await validateOrRepairClaudeJsonBlockResponse({
|
|
1063
|
-
roundArgs: args,
|
|
1064
|
-
assistantText,
|
|
1065
|
-
defaultModel,
|
|
1066
|
-
defaultEffort,
|
|
1067
|
-
sessionHandle,
|
|
1068
|
-
createRepairStream,
|
|
1069
|
-
});
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
async function runClaudeStructuredAdvisorRound(args, defaultModel, createStream = buildClaudeQueryStream, defaultEffort, sleep = defaultSleep) {
|
|
1073
|
-
if (isClaudeJsonBlockStructuredAdvisorRound(args)) {
|
|
1074
|
-
return await runClaudeJsonBlockStructuredAdvisorRound(args, defaultModel, undefined, undefined, defaultEffort, sleep);
|
|
1075
|
-
}
|
|
1076
|
-
let sessionHandle = args.resumeHandle ?? null;
|
|
1077
|
-
let apiRetryCount = 0;
|
|
1078
|
-
const apiRetryLimit = args.apiRetryLimit;
|
|
1079
|
-
while (true) {
|
|
1155
|
+
};
|
|
1080
1156
|
let result;
|
|
1081
|
-
const mirroredAssistantTexts = [];
|
|
1082
|
-
const primaryEvents = args.events
|
|
1083
|
-
? async (event) => {
|
|
1084
|
-
const text = getMirroredAssistantText(event, 'structured-advisor', args.label);
|
|
1085
|
-
if (text) {
|
|
1086
|
-
mirroredAssistantTexts.push(text);
|
|
1087
|
-
}
|
|
1088
|
-
await args.events?.(event);
|
|
1089
|
-
}
|
|
1090
|
-
: undefined;
|
|
1091
1157
|
try {
|
|
1092
1158
|
const turnAbortController = createClaudeTurnAbortController(args.signal);
|
|
1093
|
-
|
|
1094
|
-
result = await collectClaudeResult(stream, args.cwd, args.label, args.inactivityTimeoutMs, primaryEvents, 'structured-advisor', turnAbortController);
|
|
1159
|
+
result = await collectClaudeResult(run.createStream(run.args, defaultModel, defaultEffort, turnAbortController), args.cwd, args.label, args.inactivityTimeoutMs, primaryEvents, 'structured-advisor', turnAbortController);
|
|
1095
1160
|
}
|
|
1096
1161
|
catch (error) {
|
|
1097
1162
|
const providerError = normalizeClaudeProviderError(error, {
|
|
@@ -1099,7 +1164,7 @@ async function runClaudeStructuredAdvisorRound(args, defaultModel, createStream
|
|
|
1099
1164
|
sessionHandle,
|
|
1100
1165
|
});
|
|
1101
1166
|
const mirroredAssistantText = mirroredAssistantTexts.join('\n\n').trim();
|
|
1102
|
-
if (providerError.kind === 'structured_output_invalid') {
|
|
1167
|
+
if (run.protocol === 'native' && providerError.kind === 'structured_output_invalid') {
|
|
1103
1168
|
await emitProviderEvent(args.events, {
|
|
1104
1169
|
type: 'tool_progress',
|
|
1105
1170
|
provider: ANTHROPIC_CLAUDE_PROVIDER_ID,
|
|
@@ -1137,11 +1202,51 @@ async function runClaudeStructuredAdvisorRound(args, defaultModel, createStream
|
|
|
1137
1202
|
await sleep(getApiRetryDelayMs(apiRetryCount));
|
|
1138
1203
|
continue;
|
|
1139
1204
|
}
|
|
1205
|
+
if (run.protocol === 'json-block' &&
|
|
1206
|
+
mirroredAssistantText &&
|
|
1207
|
+
(providerError.kind === 'structured_output_invalid' || providerError.kind === 'structured_output_missing')) {
|
|
1208
|
+
return await validateOrRepairClaudeJsonBlockResponse({
|
|
1209
|
+
roundArgs: run.args,
|
|
1210
|
+
assistantText: mirroredAssistantText,
|
|
1211
|
+
defaultModel,
|
|
1212
|
+
defaultEffort,
|
|
1213
|
+
sessionHandle: providerError.sessionHandle ?? sessionHandle,
|
|
1214
|
+
createRepairStream: run.createRepairStream,
|
|
1215
|
+
});
|
|
1216
|
+
}
|
|
1140
1217
|
await emitClaudeProviderError(args.events, providerError, args.label);
|
|
1141
1218
|
throw providerError;
|
|
1142
1219
|
}
|
|
1143
1220
|
sessionHandle = result.sessionHandle ?? sessionHandle;
|
|
1144
1221
|
const lastResult = result.lastResult;
|
|
1222
|
+
const subtype = lastResult?.subtype ?? null;
|
|
1223
|
+
const resultErrorMessage = getClaudeResultErrorMessage(lastResult);
|
|
1224
|
+
const structuredRetryExhausted = isStructuredOutputRetryExhaustion(subtype, resultErrorMessage);
|
|
1225
|
+
const isTransientErrorResult = lastResult?.is_error === true && isTransientClaudeFailure(subtype, resultErrorMessage);
|
|
1226
|
+
if (run.protocol === 'json-block') {
|
|
1227
|
+
if (isTransientErrorResult && apiRetryCount < apiRetryLimit) {
|
|
1228
|
+
await retryAfterTransientErrorResult(subtype, resultErrorMessage);
|
|
1229
|
+
continue;
|
|
1230
|
+
}
|
|
1231
|
+
if (subtype && subtype !== 'success' && !structuredRetryExhausted) {
|
|
1232
|
+
throw await createClaudeAdvisorUnsuccessfulResultError({
|
|
1233
|
+
events: args.events,
|
|
1234
|
+
label: args.label,
|
|
1235
|
+
subtype,
|
|
1236
|
+
resultErrorMessage,
|
|
1237
|
+
sessionHandle,
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1240
|
+
const assistantText = getClaudeAssistantTextForLocalJson(result, mirroredAssistantTexts.join('\n\n').trim());
|
|
1241
|
+
return await validateOrRepairClaudeJsonBlockResponse({
|
|
1242
|
+
roundArgs: run.args,
|
|
1243
|
+
assistantText,
|
|
1244
|
+
defaultModel,
|
|
1245
|
+
defaultEffort,
|
|
1246
|
+
sessionHandle,
|
|
1247
|
+
createRepairStream: run.createRepairStream,
|
|
1248
|
+
});
|
|
1249
|
+
}
|
|
1145
1250
|
const structured = getPreferredStructuredOutput(result);
|
|
1146
1251
|
if (structured !== undefined) {
|
|
1147
1252
|
await emitProviderEvent(args.events, {
|
|
@@ -1156,9 +1261,6 @@ async function runClaudeStructuredAdvisorRound(args, defaultModel, createStream
|
|
|
1156
1261
|
structured,
|
|
1157
1262
|
};
|
|
1158
1263
|
}
|
|
1159
|
-
const subtype = lastResult?.subtype ?? null;
|
|
1160
|
-
const resultErrorMessage = getClaudeResultErrorMessage(lastResult);
|
|
1161
|
-
const structuredRetryExhausted = isStructuredOutputRetryExhaustion(subtype, resultErrorMessage);
|
|
1162
1264
|
if (structuredRetryExhausted) {
|
|
1163
1265
|
await emitProviderEvent(args.events, {
|
|
1164
1266
|
type: 'tool_progress',
|
|
@@ -1177,40 +1279,28 @@ async function runClaudeStructuredAdvisorRound(args, defaultModel, createStream
|
|
|
1177
1279
|
},
|
|
1178
1280
|
});
|
|
1179
1281
|
}
|
|
1180
|
-
if (
|
|
1181
|
-
|
|
1182
|
-
await emitProviderEvent(args.events, {
|
|
1183
|
-
type: 'tool_progress',
|
|
1184
|
-
provider: ANTHROPIC_CLAUDE_PROVIDER_ID,
|
|
1185
|
-
role: 'structured-advisor',
|
|
1186
|
-
label: args.label,
|
|
1187
|
-
sessionHandle,
|
|
1188
|
-
toolName: 'api_retry',
|
|
1189
|
-
message: `transient Claude error result; retrying review (${apiRetryCount}/${apiRetryLimit})`,
|
|
1190
|
-
isError: true,
|
|
1191
|
-
providerData: {
|
|
1192
|
-
retryCount: apiRetryCount,
|
|
1193
|
-
retryLimit: apiRetryLimit,
|
|
1194
|
-
subtype,
|
|
1195
|
-
message: resultErrorMessage,
|
|
1196
|
-
},
|
|
1197
|
-
});
|
|
1198
|
-
await sleep(getApiRetryDelayMs(apiRetryCount));
|
|
1282
|
+
if (isTransientErrorResult && apiRetryCount < apiRetryLimit) {
|
|
1283
|
+
await retryAfterTransientErrorResult(subtype, resultErrorMessage);
|
|
1199
1284
|
continue;
|
|
1200
1285
|
}
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
: `Claude ${args.label} did not return a successful result${subtype ? ` (${subtype})` : ''}`,
|
|
1205
|
-
role: 'structured-advisor',
|
|
1206
|
-
sessionHandle,
|
|
1207
|
-
kind: getClaudeStructuredFailureKind(subtype, resultErrorMessage),
|
|
1286
|
+
throw await createClaudeAdvisorUnsuccessfulResultError({
|
|
1287
|
+
events: args.events,
|
|
1288
|
+
label: args.label,
|
|
1208
1289
|
subtype,
|
|
1290
|
+
resultErrorMessage,
|
|
1291
|
+
sessionHandle,
|
|
1209
1292
|
});
|
|
1210
|
-
await emitClaudeProviderError(args.events, providerError, args.label);
|
|
1211
|
-
throw providerError;
|
|
1212
1293
|
}
|
|
1213
1294
|
}
|
|
1295
|
+
async function runClaudeJsonBlockStructuredAdvisorRound(args, defaultModel, createStream = buildClaudeJsonBlockQueryStream, createRepairStream = buildClaudeJsonBlockRepairQueryStream, defaultEffort, sleep = defaultSleep) {
|
|
1296
|
+
return runClaudeStructuredAdvisorRoundLoop({ protocol: 'json-block', args, createStream, createRepairStream }, defaultModel, defaultEffort, sleep);
|
|
1297
|
+
}
|
|
1298
|
+
async function runClaudeStructuredAdvisorRound(args, defaultModel, createStream = buildClaudeQueryStream, defaultEffort, sleep = defaultSleep) {
|
|
1299
|
+
if (isClaudeJsonBlockStructuredAdvisorRound(args)) {
|
|
1300
|
+
return await runClaudeJsonBlockStructuredAdvisorRound(args, defaultModel, undefined, undefined, defaultEffort, sleep);
|
|
1301
|
+
}
|
|
1302
|
+
return runClaudeStructuredAdvisorRoundLoop({ protocol: 'native', args, createStream }, defaultModel, defaultEffort, sleep);
|
|
1303
|
+
}
|
|
1214
1304
|
class AnthropicClaudeStructuredAdvisorAdapter {
|
|
1215
1305
|
options;
|
|
1216
1306
|
constructor(options = {}) {
|
|
@@ -1264,45 +1354,27 @@ function buildClaudeWritePathGuardHooks(cwd, allowedWritePaths) {
|
|
|
1264
1354
|
return { PreToolUse: [{ hooks: [guard] }] };
|
|
1265
1355
|
}
|
|
1266
1356
|
function buildClaudeCoderQueryOptions(args, defaultModel, claudeExecutablePath = getClaudeCodeExecutablePath(), defaultEffort, abortController = deriveClaudeAbortController(args.signal)) {
|
|
1267
|
-
return {
|
|
1357
|
+
return buildClaudeCoreQueryOptions({
|
|
1268
1358
|
cwd: args.cwd,
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1359
|
+
abortController,
|
|
1360
|
+
model: defaultModel,
|
|
1361
|
+
effort: defaultEffort,
|
|
1272
1362
|
// `toolPolicy.allowRun: false` (plan-authoring turns) removes Bash so the
|
|
1273
1363
|
// "no shell" jail is enforced by tool absence, not prompt text.
|
|
1274
1364
|
tools: args.toolPolicy?.allowRun === false
|
|
1275
1365
|
? ['Read', 'Grep', 'Glob', 'Edit', 'Write']
|
|
1276
1366
|
: ['Read', 'Grep', 'Glob', 'Bash', 'Edit', 'Write'],
|
|
1277
|
-
|
|
1278
|
-
?
|
|
1279
|
-
:
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
schema: args.outputSchema,
|
|
1289
|
-
},
|
|
1290
|
-
}
|
|
1291
|
-
: {}),
|
|
1292
|
-
stderr: (data) => {
|
|
1293
|
-
void args.events?.({
|
|
1294
|
-
type: 'tool_progress',
|
|
1295
|
-
provider: ANTHROPIC_CLAUDE_PROVIDER_ID,
|
|
1296
|
-
role: 'coder',
|
|
1297
|
-
label: 'coder',
|
|
1298
|
-
sessionHandle: args.resumeHandle ?? null,
|
|
1299
|
-
toolName: 'stderr',
|
|
1300
|
-
message: data,
|
|
1301
|
-
isError: true,
|
|
1302
|
-
providerData: { stream: 'stderr' },
|
|
1303
|
-
});
|
|
1304
|
-
},
|
|
1305
|
-
};
|
|
1367
|
+
hooks: args.toolPolicy?.allowedWritePaths
|
|
1368
|
+
? buildClaudeWritePathGuardHooks(args.cwd, args.toolPolicy.allowedWritePaths)
|
|
1369
|
+
: undefined,
|
|
1370
|
+
resumeHandle: args.resumeHandle,
|
|
1371
|
+
claudeExecutablePath,
|
|
1372
|
+
outputSchema: args.outputSchema,
|
|
1373
|
+
events: args.events,
|
|
1374
|
+
stderrRole: 'coder',
|
|
1375
|
+
stderrLabel: 'coder',
|
|
1376
|
+
stderrSessionHandle: args.resumeHandle ?? null,
|
|
1377
|
+
});
|
|
1306
1378
|
}
|
|
1307
1379
|
function buildClaudeCoderQueryStream(args, defaultModel, defaultEffort, abortController) {
|
|
1308
1380
|
return query({
|
|
@@ -1311,29 +1383,18 @@ function buildClaudeCoderQueryStream(args, defaultModel, defaultEffort, abortCon
|
|
|
1311
1383
|
});
|
|
1312
1384
|
}
|
|
1313
1385
|
function buildClaudeCoderRepairQueryOptions(args, defaultModel, claudeExecutablePath = getClaudeCodeExecutablePath(), defaultEffort, abortController) {
|
|
1314
|
-
return {
|
|
1386
|
+
return buildClaudeCoreQueryOptions({
|
|
1315
1387
|
cwd: args.cwd,
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1388
|
+
abortController,
|
|
1389
|
+
model: defaultModel,
|
|
1390
|
+
effort: defaultEffort,
|
|
1319
1391
|
tools: [],
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
provider: ANTHROPIC_CLAUDE_PROVIDER_ID,
|
|
1327
|
-
role: 'coder',
|
|
1328
|
-
label: `${args.label}:structured-json-repair`,
|
|
1329
|
-
sessionHandle: null,
|
|
1330
|
-
toolName: 'stderr',
|
|
1331
|
-
message: data,
|
|
1332
|
-
isError: true,
|
|
1333
|
-
providerData: { stream: 'stderr' },
|
|
1334
|
-
});
|
|
1335
|
-
},
|
|
1336
|
-
};
|
|
1392
|
+
claudeExecutablePath,
|
|
1393
|
+
events: args.events,
|
|
1394
|
+
stderrRole: 'coder',
|
|
1395
|
+
stderrLabel: `${args.label}:structured-json-repair`,
|
|
1396
|
+
stderrSessionHandle: null,
|
|
1397
|
+
});
|
|
1337
1398
|
}
|
|
1338
1399
|
function buildClaudeCoderRepairQueryStream(args, repairPrompt, defaultModel, defaultEffort, abortController) {
|
|
1339
1400
|
return query({
|