@@ -589,9 +589,10 @@ function ensureDangerousGitHook(targetClaudeDir = null) {
589589 * Spawn claude-zeroshots process and stream output via message bus
590590 * @param {Object } agent - Agent instance
591591 * @param {String } context - Context to pass to Claude
592+ * @param {{skipStructuredResultCheck?: boolean} } [options] - Internal nested-task controls
592593 * @returns {Promise<Object> } Result object { success, output, error }
593594 */
594- async function spawnClaudeTask ( agent , context ) {
595+ async function spawnClaudeTask ( agent , context , options = { } ) {
595596 const providerName = agent . _resolveProvider ? agent . _resolveProvider ( ) : 'claude' ;
596597 const modelSpec = resolveAgentModelSpec ( agent ) ;
597598
@@ -628,7 +629,7 @@ async function spawnClaudeTask(agent, context) {
628629
629630 // MOCK SUPPORT: Use injected mock function if provided
630631 if ( agent . mockSpawnFn ) {
631- return agent . mockSpawnFn ( args , { context } ) ;
632+ return agent . mockSpawnFn ( args , { context, options } ) ;
632633 }
633634
634635 // SAFETY: Fail hard if testMode=true but no mock (should be caught in constructor)
@@ -641,7 +642,7 @@ async function spawnClaudeTask(agent, context) {
641642
642643 // ISOLATION MODE: Run inside Docker container
643644 if ( agent . isolation ?. enabled ) {
644- return spawnClaudeTaskIsolated ( agent , context ) ;
645+ return spawnClaudeTaskIsolated ( agent , context , options ) ;
645646 }
646647
647648 // NON-ISOLATION MODE: For Claude, use user's existing Claude config
@@ -703,7 +704,7 @@ async function spawnClaudeTask(agent, context) {
703704 }
704705
705706 // Now follow the logs and stream output
706- return followClaudeTaskLogs ( agent , taskId ) ;
707+ return followClaudeTaskLogs ( agent , taskId , options ) ;
707708}
708709
709710function resolveAgentModelSpec ( agent ) {
@@ -1212,7 +1213,9 @@ function buildFailureContext({ agent, taskId, providerName, state, stdout }) {
12121213}
12131214
12141215async function buildCompletionResult ( { agent, taskId, providerName, state, stdout, success } ) {
1215- const classified = await evaluateStructuredSuccess ( { agent, taskId, state, success } ) ;
1216+ const classified = state . skipStructuredResultCheck
1217+ ? { success, error : null }
1218+ : await evaluateStructuredSuccess ( { agent, taskId, state, success } ) ;
12161219 let errorContext = classified . error ;
12171220 if ( ! errorContext && ! classified . success ) {
12181221 errorContext = buildFailureContext ( { agent, taskId, providerName, state, stdout } ) ;
@@ -1401,9 +1404,17 @@ function buildKillHandler({ agent, taskId, state, providerName, resolve }) {
14011404 } ;
14021405}
14031406
1404- function createLogFollower ( { agent, taskId, fsModule, ctPath, providerName } ) {
1407+ function createLogFollower ( {
1408+ agent,
1409+ taskId,
1410+ fsModule,
1411+ ctPath,
1412+ providerName,
1413+ skipStructuredResultCheck = false ,
1414+ } ) {
14051415 return new Promise ( ( resolve ) => {
14061416 const state = createLogFollowState ( ) ;
1417+ state . skipStructuredResultCheck = skipStructuredResultCheck ;
14071418
14081419 state . logFilePath = lookupLogFilePath ( ctPath , taskId ) ;
14091420 if ( state . logFilePath ) {
@@ -1463,12 +1474,19 @@ function createLogFollower({ agent, taskId, fsModule, ctPath, providerName }) {
14631474 * @param {String } taskId - Task ID to follow
14641475 * @returns {Promise<Object> } Result object { success, output, error }
14651476 */
1466- function followClaudeTaskLogs ( agent , taskId ) {
1477+ function followClaudeTaskLogs ( agent , taskId , options = { } ) {
14671478 const fsModule = require ( 'fs' ) ;
14681479 const ctPath = getClaudeTasksPath ( ) ;
14691480 const providerName = agent . _resolveProvider ? agent . _resolveProvider ( ) : 'claude' ;
14701481
1471- return createLogFollower ( { agent, taskId, fsModule, ctPath, providerName } ) ;
1482+ return createLogFollower ( {
1483+ agent,
1484+ taskId,
1485+ fsModule,
1486+ ctPath,
1487+ providerName,
1488+ skipStructuredResultCheck : options . skipStructuredResultCheck === true ,
1489+ } ) ;
14721490}
14731491
14741492// Cache zeroshot path at module load time (when PATH is correct)
@@ -1506,9 +1524,10 @@ function getClaudeTasksPath() {
15061524 * Runs Claude CLI inside the container for full isolation
15071525 * @param {Object } agent - Agent instance
15081526 * @param {String } context - Context to pass to Claude
1527+ * @param {{skipStructuredResultCheck?: boolean} } [options] - Internal nested-task controls
15091528 * @returns {Promise<Object> } Result object { success, output, error }
15101529 */
1511- async function spawnClaudeTaskIsolated ( agent , context ) {
1530+ async function spawnClaudeTaskIsolated ( agent , context , options = { } ) {
15121531 const { manager, clusterId } = agent . isolation ;
15131532 const providerName = agent . _resolveProvider ? agent . _resolveProvider ( ) : 'claude' ;
15141533 const modelSpec = resolveAgentModelSpec ( agent ) ;
@@ -1626,7 +1645,7 @@ async function spawnClaudeTaskIsolated(agent, context) {
16261645
16271646 // STEP 2: Install the lifecycle-owned handle before liveness monitoring can
16281647 // observe the task, then follow the task's log file inside the container.
1629- const execution = followClaudeTaskLogsIsolated ( agent , taskId ) ;
1648+ const execution = followClaudeTaskLogsIsolated ( agent , taskId , options ) ;
16301649 if ( agent . enableLivenessCheck ) {
16311650 agent . taskStartedAt = Date . now ( ) ;
16321651 agent . lastOutputTime = agent . taskStartedAt ;
@@ -1658,7 +1677,7 @@ async function spawnClaudeTaskIsolated(agent, context) {
16581677 * - Status checks reduced to every 2 seconds (not every poll)
16591678 * - Result: 10-20% overall latency reduction
16601679 */
1661- function createIsolatedLogState ( ) {
1680+ function createIsolatedLogState ( skipStructuredResultCheck = false ) {
16621681 return {
16631682 taskExited : false ,
16641683 resolved : false ,
@@ -1670,6 +1689,7 @@ function createIsolatedLogState() {
16701689 statusCheckInterval : null ,
16711690 timeoutTimer : null ,
16721691 lineBuffer : '' ,
1692+ skipStructuredResultCheck,
16731693 } ;
16741694}
16751695
@@ -1816,7 +1836,9 @@ function settleIsolatedTerminalStatus({
18161836 } ,
18171837 } )
18181838 : null ;
1819- const parsedResult = await agent . _parseResultOutput ( state . fullOutput ) ;
1839+ const parsedResult = state . skipStructuredResultCheck
1840+ ? null
1841+ : await agent . _parseResultOutput ( state . fullOutput ) ;
18201842
18211843 settleIsolatedFollower ( {
18221844 agent,
@@ -2056,7 +2078,7 @@ function startIsolatedStatusChecks({
20562078 } , 2000 ) ;
20572079}
20582080
2059- function followClaudeTaskLogsIsolated ( agent , taskId ) {
2081+ function followClaudeTaskLogsIsolated ( agent , taskId , options = { } ) {
20602082 const { isolation } = agent ;
20612083 if ( ! isolation ?. manager ) {
20622084 throw new Error ( 'followClaudeTaskLogsIsolated: isolation manager not found' ) ;
@@ -2067,7 +2089,7 @@ function followClaudeTaskLogsIsolated(agent, taskId) {
20672089 const providerName = agent . _resolveProvider ? agent . _resolveProvider ( ) : 'claude' ;
20682090
20692091 return new Promise ( ( resolve , reject ) => {
2070- const state = createIsolatedLogState ( ) ;
2092+ const state = createIsolatedLogState ( options . skipStructuredResultCheck === true ) ;
20712093 const cleanup = buildIsolatedCleanup ( state ) ;
20722094 const onLine = ( line ) => broadcastIsolatedLine ( { agent, providerName, taskId, line } ) ;
20732095 state . lifecycleHandle = buildIsolatedLifecycleHandle ( {
@@ -2203,6 +2225,8 @@ async function parseResultOutput(agent, output) {
22032225 schema : agent . config . jsonSchema ,
22042226 providerName,
22052227 isCancelled : ( ) => agent . running === false || agent . state === 'stopped' ,
2228+ runReformat : ( prompt ) =>
2229+ agent . _spawnClaudeTask ( prompt , { skipStructuredResultCheck : true } ) ,
22062230 onAttempt : ( attempt , lastError ) => {
22072231 if ( lastError ) {
22082232 console . warn ( `[Agent ${ agent . id } ] Reformat attempt ${ attempt } : ${ lastError } ` ) ;
@@ -2214,6 +2238,7 @@ async function parseResultOutput(agent, output) {
22142238 } ,
22152239 } ) ;
22162240 } catch ( reformatError ) {
2241+ if ( reformatError . code === 'REFORMAT_CANCELLED' ) throw reformatError ;
22172242 // Reformatting failed - fall through to error below
22182243 console . error ( `[Agent ${ agent . id } ] Reformatting failed: ${ reformatError . message } ` ) ;
22192244 }
0 commit comments