fix: integrate audit findings round 1 - tools, worker, scheduler, main agent
- Unify ToolResultEnvelope (output vs content) for built-in tools - Fix shell.run AsyncGenerator consumption in ToolRegistry.call/streaming - Scheduler: consume WorkerResult.status instead of marking all running tasks completed - WorkerProcess/WorkerManager: surface exit events and generate failed/cancelled result - MainAgent: integrate ContextAssembler, Chinese destructive regex, ArchitectureDesigner impact gate - run.ts: pendingConfirmation flow, dispatch extracted, .air files filtered from /results - CapabilityRegistry wired into RuntimeApp and ServiceRegistry; DoctorService uses it - release.ts: findRepoRoot/findBun, run air e2e + depcruise + runtime regression - New gates: release-critical-gates, CLI run command regression - 14/14 e2e gates pass; 3/3 release dry-run pass Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -31,7 +31,7 @@ export interface WorkerHandle {
|
||||
worker_id: string
|
||||
process: WorkerProcess
|
||||
config: WorkerConfig
|
||||
state: 'starting' | 'ready' | 'running' | 'completed' | 'error' | 'cancelled'
|
||||
state: 'starting' | 'ready' | 'running' | 'completed' | 'failed' | 'error' | 'cancelled'
|
||||
started_at: string
|
||||
completed_at?: string
|
||||
result?: WorkerResult<unknown>
|
||||
@@ -107,6 +107,8 @@ export class WorkerManager {
|
||||
|
||||
proc.set_process(child)
|
||||
|
||||
proc.on_exit((exit) => this.handle_worker_exit(config.agent_id, exit))
|
||||
|
||||
// Set up handlers for worker IPC messages
|
||||
this.setup_worker_handlers(proc, config.agent_id)
|
||||
|
||||
@@ -179,10 +181,10 @@ export class WorkerManager {
|
||||
{
|
||||
session_id: this.execution_context?.session_id || msg.session_id,
|
||||
project_id: this.execution_context?.project_id || '',
|
||||
project_root: this.execution_context?.project_root || process.cwd(),
|
||||
agent_id,
|
||||
permission_template: 'executor',
|
||||
cwd: this.execution_context?.project_root || process.cwd()
|
||||
} as any
|
||||
agent_type: 'executor',
|
||||
}
|
||||
)
|
||||
|
||||
this.send_to_worker(agent_id, 'tool.result', {
|
||||
@@ -238,8 +240,11 @@ export class WorkerManager {
|
||||
proc.on_message('worker.result', (msg) => {
|
||||
const handle = this.workers.get(agent_id)
|
||||
if (handle) {
|
||||
handle.state = 'completed'
|
||||
handle.result = this.wrap_worker_result(msg.payload, handle)
|
||||
handle.state = handle.result.status === 'completed' ? 'completed'
|
||||
: handle.result.status === 'cancelled' ? 'cancelled'
|
||||
: 'failed'
|
||||
handle.completed_at = new Date().toISOString()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -313,29 +318,84 @@ export class WorkerManager {
|
||||
return handle.result
|
||||
}
|
||||
|
||||
/**
|
||||
* Get result for a task.
|
||||
*/
|
||||
get_result_for_task(task_id: string): WorkerResult<unknown> | undefined {
|
||||
return this.list().find(w => w.config.task_spec?.id === task_id)?.result
|
||||
}
|
||||
|
||||
/**
|
||||
* Get handle for a task.
|
||||
*/
|
||||
get_handle_for_task(task_id: string): WorkerHandle | undefined {
|
||||
return this.list().find(w => w.config.task_spec?.id === task_id)
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Private
|
||||
// ============================================================================
|
||||
|
||||
private handle_worker_exit(agent_id: string, exit: { code: number | null; signal: NodeJS.Signals | null; semantic: string; description: string }): void {
|
||||
const handle = this.workers.get(agent_id)
|
||||
if (!handle) return
|
||||
if (handle.result) return
|
||||
|
||||
const task_id = (handle.config.task_spec?.id as string) || `${agent_id}_task`
|
||||
const cancelled = exit.semantic === 'parent_cancelled'
|
||||
handle.state = cancelled ? 'cancelled' : 'failed'
|
||||
handle.completed_at = new Date().toISOString()
|
||||
handle.result = {
|
||||
task_id: task_id as any,
|
||||
agent_id: handle.config.agent_id as any,
|
||||
agent_type: 'executor',
|
||||
status: cancelled ? 'cancelled' : 'failed',
|
||||
summary: `Worker exited without result: ${exit.semantic}`,
|
||||
changed_files: [],
|
||||
artifacts: [],
|
||||
verification: [],
|
||||
risks: [],
|
||||
follow_up_tasks: [],
|
||||
evidence_refs: [],
|
||||
result: { exit },
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap a raw worker payload into a properly typed WorkerResult envelope.
|
||||
* Provides safe defaults for any missing fields.
|
||||
*/
|
||||
private wrap_worker_result(payload: Record<string, unknown>, handle: WorkerHandle): WorkerResult<unknown> {
|
||||
const raw_status = (payload.status as string) || 'completed'
|
||||
const status = raw_status === 'completed' || raw_status === 'cancelled' || raw_status === 'blocked' || raw_status === 'failed'
|
||||
? raw_status
|
||||
: raw_status === 'fixed' || raw_status === 'pass'
|
||||
? 'completed'
|
||||
: 'failed'
|
||||
const changes = Array.isArray((payload as any).changes) ? (payload as any).changes : []
|
||||
const changed_files = (payload.changed_files as string[] | undefined) || changes.map((c: any) => String(c.file)).filter(Boolean)
|
||||
const verification_payload = payload.verification as any
|
||||
const verification = Array.isArray(verification_payload) ? verification_payload
|
||||
: verification_payload ? [{ command: 'worker verification', passed: Boolean(verification_payload.passed), output: String(verification_payload.output || '') }] as any[]
|
||||
: []
|
||||
const summary = (payload.summary as string)
|
||||
|| (payload.error ? String(payload.error) : '')
|
||||
|| (changed_files.length > 0 ? `Changed files: ${changed_files.join(', ')}` : `Worker ${status}`)
|
||||
|
||||
return {
|
||||
task_id: (payload.task_id as string) || '' as any,
|
||||
task_id: (payload.task_id as string) || (handle.config.task_spec?.id as string) || '' as any,
|
||||
agent_id: (payload.agent_id as string) || handle.config.agent_id as any,
|
||||
agent_type: (payload.agent_type as AgentType) || 'executor',
|
||||
status: (payload.status as WorkerStatus) || 'completed',
|
||||
summary: (payload.summary as string) || '',
|
||||
changed_files: (payload.changed_files as string[]) || [],
|
||||
status: status as WorkerStatus,
|
||||
summary,
|
||||
changed_files,
|
||||
diff_ref: (payload.diff_ref as string | undefined) || undefined,
|
||||
artifacts: (payload.artifacts as any[]) || [],
|
||||
verification: (payload.verification as any[]) || [],
|
||||
verification,
|
||||
risks: (payload.risks as any[]) || [],
|
||||
follow_up_tasks: (payload.follow_up_tasks as any[]) || [],
|
||||
evidence_refs: (payload.evidence_refs as any[]) || [],
|
||||
result: (payload.result as unknown) || null,
|
||||
result: (payload.result as unknown) || payload,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user