patchwarden 1.5.1 → 1.6.2
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/PatchWarden.cmd +32 -2
- package/README.en.md +158 -26
- package/README.md +124 -24
- package/dist/assessments/agentAssessor.js +21 -12
- package/dist/assessments/assessmentDiagnostics.d.ts +29 -0
- package/dist/assessments/assessmentDiagnostics.js +132 -0
- package/dist/assessments/assessmentStore.d.ts +39 -0
- package/dist/assessments/assessmentStore.js +207 -45
- package/dist/assessments/securitySnapshot.d.ts +50 -0
- package/dist/assessments/securitySnapshot.js +158 -0
- package/dist/config.d.ts +15 -0
- package/dist/config.js +66 -13
- package/dist/control/fileManager.d.ts +2 -0
- package/dist/control/fileManager.js +39 -0
- package/dist/control/middleware/auth.d.ts +6 -0
- package/dist/control/middleware/auth.js +14 -5
- package/dist/control/middleware/static.js +16 -3
- package/dist/control/routeTable.d.ts +24 -0
- package/dist/control/routeTable.js +263 -0
- package/dist/control/routes/audit.js +17 -7
- package/dist/control/routes/evidence.js +1 -1
- package/dist/control/routes/lineage.js +1 -1
- package/dist/control/routes/process.d.ts +7 -0
- package/dist/control/routes/process.js +329 -42
- package/dist/control/routes/sessions.d.ts +9 -1
- package/dist/control/routes/sessions.js +84 -21
- package/dist/control/routes/status.d.ts +60 -1
- package/dist/control/routes/status.js +94 -15
- package/dist/control/routes/taskActions.js +24 -60
- package/dist/control/routes/tasks.d.ts +4 -0
- package/dist/control/routes/tasks.js +61 -62
- package/dist/control/routes/workspace.js +50 -23
- package/dist/control/runtime.d.ts +3 -2
- package/dist/control/runtime.js +65 -40
- package/dist/control/server.js +37 -365
- package/dist/control/shared.d.ts +25 -1
- package/dist/control/shared.js +64 -11
- package/dist/diagnostics/allowedTestCommandSafety.d.ts +1 -0
- package/dist/diagnostics/allowedTestCommandSafety.js +11 -0
- package/dist/direct/directAudit.js +234 -225
- package/dist/direct/directGuards.d.ts +2 -1
- package/dist/direct/directGuards.js +42 -29
- package/dist/direct/directPatch.d.ts +6 -1
- package/dist/direct/directPatch.js +55 -8
- package/dist/direct/directSessionStore.d.ts +16 -6
- package/dist/direct/directSessionStore.js +128 -35
- package/dist/direct/directVerification.js +8 -16
- package/dist/doctor.js +65 -57
- package/dist/goal/acceptanceEngine.d.ts +1 -1
- package/dist/goal/goalProgress.js +75 -69
- package/dist/goal/goalReport.js +5 -12
- package/dist/goal/goalStatus.d.ts +2 -0
- package/dist/goal/goalStatus.js +10 -3
- package/dist/goal/goalStore.d.ts +9 -2
- package/dist/goal/goalStore.js +152 -45
- package/dist/goal/handoffExport.js +6 -6
- package/dist/goal/specKitImport.js +97 -96
- package/dist/goal/subgoalSync.js +11 -9
- package/dist/goal/worktreeManager.d.ts +2 -0
- package/dist/goal/worktreeManager.js +209 -136
- package/dist/httpServer.js +111 -32
- package/dist/logging.js +5 -3
- package/dist/policy/projectPolicy.js +5 -2
- package/dist/release/releaseGate.d.ts +1 -1
- package/dist/release/releaseGate.js +78 -25
- package/dist/runner/agentInvocation.d.ts +14 -0
- package/dist/runner/agentInvocation.js +96 -4
- package/dist/runner/changeCapture.d.ts +13 -5
- package/dist/runner/changeCapture.js +243 -61
- package/dist/runner/postTaskCleanup.js +48 -19
- package/dist/runner/processSecurity.d.ts +56 -0
- package/dist/runner/processSecurity.js +373 -0
- package/dist/runner/runTask.d.ts +2 -1
- package/dist/runner/runTask.js +162 -93
- package/dist/runner/simpleProcess.d.ts +3 -0
- package/dist/runner/simpleProcess.js +50 -36
- package/dist/{taskProgress.d.ts → runner/taskProgress.d.ts} +1 -1
- package/dist/{taskProgress.js → runner/taskProgress.js} +2 -2
- package/dist/{taskRuntime.d.ts → runner/taskRuntime.d.ts} +3 -1
- package/dist/{taskRuntime.js → runner/taskRuntime.js} +3 -2
- package/dist/runner/taskStatusStore.d.ts +11 -0
- package/dist/runner/taskStatusStore.js +35 -0
- package/dist/runner/watch.d.ts +14 -1
- package/dist/runner/watch.js +320 -49
- package/dist/security/contentRedaction.js +2 -2
- package/dist/security/discoveryTokenStore.d.ts +3 -2
- package/dist/security/discoveryTokenStore.js +17 -2
- package/dist/security/loopbackHost.d.ts +2 -0
- package/dist/security/loopbackHost.js +16 -0
- package/dist/security/pathGuard.js +32 -17
- package/dist/security/planGuard.js +2 -1
- package/dist/security/runtimeGuard.js +9 -6
- package/dist/security/secretComparison.d.ts +3 -0
- package/dist/security/secretComparison.js +11 -0
- package/dist/security/sensitiveGuard.d.ts +5 -0
- package/dist/security/sensitiveGuard.js +27 -8
- package/dist/security/toolInvocationGuard.d.ts +2 -2
- package/dist/security/toolInvocationGuard.js +1 -1
- package/dist/security/workspaceRootGuard.d.ts +8 -0
- package/dist/security/workspaceRootGuard.js +47 -0
- package/dist/smoke-test.js +27 -26
- package/dist/tools/catalog/index.d.ts +4 -0
- package/dist/tools/catalog/index.js +4 -0
- package/dist/tools/{toolCatalog.js → catalog/toolCatalog.js} +3 -12
- package/dist/tools/{toolRegistry.d.ts → catalog/toolRegistry.d.ts} +3 -3
- package/dist/tools/{toolRegistry.js → catalog/toolRegistry.js} +6 -15
- package/dist/tools/definitions/toolDefs.d.ts +18 -0
- package/dist/tools/definitions/toolDefs.js +1177 -0
- package/dist/tools/{auditSession.d.ts → diagnostics/auditSession.d.ts} +1 -1
- package/dist/tools/{auditSession.js → diagnostics/auditSession.js} +2 -2
- package/dist/tools/{auditTask.d.ts → diagnostics/auditTask.d.ts} +2 -1
- package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +134 -36
- package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +18 -6
- package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +36 -8
- package/dist/tools/diagnostics/index.d.ts +6 -0
- package/dist/tools/diagnostics/index.js +6 -0
- package/dist/tools/{safeStatus.d.ts → diagnostics/safeStatus.d.ts} +3 -3
- package/dist/tools/{safeStatus.js → diagnostics/safeStatus.js} +6 -6
- package/dist/tools/{safeViews.d.ts → diagnostics/safeViews.d.ts} +12 -8
- package/dist/tools/{safeViews.js → diagnostics/safeViews.js} +23 -13
- package/dist/tools/{schemaDriftCheck.d.ts → diagnostics/schemaDriftCheck.d.ts} +1 -1
- package/dist/tools/{schemaDriftCheck.js → diagnostics/schemaDriftCheck.js} +2 -2
- package/dist/tools/{createDirectSession.js → direct/createDirectSession.js} +6 -6
- package/dist/tools/{finalizeDirectSession.d.ts → direct/finalizeDirectSession.d.ts} +1 -1
- package/dist/tools/direct/finalizeDirectSession.js +86 -0
- package/dist/tools/direct/index.d.ts +3 -0
- package/dist/tools/direct/index.js +3 -0
- package/dist/tools/{runDirectVerificationBundle.js → direct/runDirectVerificationBundle.js} +2 -2
- package/dist/tools/{discoverTools.d.ts → discovery/discoverTools.d.ts} +2 -2
- package/dist/tools/{discoverTools.js → discovery/discoverTools.js} +5 -5
- package/dist/tools/{explainTool.d.ts → discovery/explainTool.d.ts} +2 -2
- package/dist/tools/{explainTool.js → discovery/explainTool.js} +2 -2
- package/dist/tools/discovery/index.d.ts +3 -0
- package/dist/tools/discovery/index.js +3 -0
- package/dist/tools/{invokeDiscoveredTool.d.ts → discovery/invokeDiscoveredTool.d.ts} +2 -2
- package/dist/tools/{invokeDiscoveredTool.js → discovery/invokeDiscoveredTool.js} +5 -5
- package/dist/tools/dispatch/coreDispatch.js +32 -31
- package/dist/tools/dispatch/diagnosticDispatch.d.ts +3 -5
- package/dist/tools/dispatch/diagnosticDispatch.js +70 -68
- package/dist/tools/dispatch/directDispatch.js +11 -10
- package/dist/tools/dispatch/goalDispatch.js +5 -4
- package/dist/tools/dispatch/releaseDispatch.js +3 -2
- package/dist/tools/dispatch/validation.d.ts +7 -0
- package/dist/tools/dispatch/validation.js +71 -0
- package/dist/tools/{getPlan.js → goals/getPlan.js} +4 -4
- package/dist/tools/{goalSubgoalTask.d.ts → goals/goalSubgoalTask.d.ts} +1 -1
- package/dist/tools/goals/goalSubgoalTask.js +118 -0
- package/dist/tools/goals/index.d.ts +3 -0
- package/dist/tools/goals/index.js +3 -0
- package/dist/tools/{savePlan.js → goals/savePlan.js} +28 -8
- package/dist/tools/registry.d.ts +7 -11
- package/dist/tools/registry.js +17 -1178
- package/dist/tools/{checkReleaseGate.d.ts → release/checkReleaseGate.d.ts} +1 -1
- package/dist/tools/{checkReleaseGate.js → release/checkReleaseGate.js} +3 -3
- package/dist/tools/release/index.d.ts +2 -0
- package/dist/tools/release/index.js +2 -0
- package/dist/tools/{releaseMode.d.ts → release/releaseMode.d.ts} +2 -2
- package/dist/tools/{releaseMode.js → release/releaseMode.js} +46 -26
- package/dist/tools/tasks/cancelTask.d.ts +11 -0
- package/dist/tools/tasks/cancelTask.js +86 -0
- package/dist/tools/{createTask.d.ts → tasks/createTask.d.ts} +12 -4
- package/dist/tools/{createTask.js → tasks/createTask.js} +110 -34
- package/dist/tools/{diagnoseTask.d.ts → tasks/diagnoseTask.d.ts} +1 -1
- package/dist/tools/{diagnoseTask.js → tasks/diagnoseTask.js} +12 -16
- package/dist/tools/{evidencePack.js → tasks/evidencePack.js} +34 -22
- package/dist/tools/{getTaskFile.js → tasks/getTaskFile.js} +4 -4
- package/dist/tools/{getTaskStatus.d.ts → tasks/getTaskStatus.d.ts} +6 -1
- package/dist/tools/{getTaskStatus.js → tasks/getTaskStatus.js} +11 -6
- package/dist/tools/{getTaskStdoutTail.js → tasks/getTaskStdoutTail.js} +13 -11
- package/dist/tools/{getTaskSummary.js → tasks/getTaskSummary.js} +35 -36
- package/dist/tools/tasks/index.d.ts +18 -0
- package/dist/tools/tasks/index.js +18 -0
- package/dist/tools/tasks/killTask.d.ts +1 -0
- package/dist/tools/{listTasks.d.ts → tasks/listTasks.d.ts} +1 -1
- package/dist/tools/{listTasks.js → tasks/listTasks.js} +5 -4
- package/dist/tools/{reconcileTasks.d.ts → tasks/reconcileTasks.d.ts} +3 -2
- package/dist/tools/tasks/reconcileTasks.js +496 -0
- package/dist/tools/tasks/retryTask.js +60 -0
- package/dist/tools/{runTaskLoop.d.ts → tasks/runTaskLoop.d.ts} +6 -6
- package/dist/tools/{runTaskLoop.js → tasks/runTaskLoop.js} +63 -41
- package/dist/tools/tasks/runVerification.js +38 -0
- package/dist/tools/{taskLineage.js → tasks/taskLineage.js} +8 -7
- package/dist/tools/{taskOutputs.d.ts → tasks/taskOutputs.d.ts} +2 -2
- package/dist/tools/{taskOutputs.js → tasks/taskOutputs.js} +17 -16
- package/dist/tools/tasks/taskStates.d.ts +6 -0
- package/dist/tools/tasks/taskStates.js +52 -0
- package/dist/tools/{waitForTask.js → tasks/waitForTask.js} +3 -11
- package/dist/tools/{androidDoctor.js → workspace/androidDoctor.js} +16 -10
- package/dist/tools/{applyPatch.d.ts → workspace/applyPatch.d.ts} +1 -1
- package/dist/tools/workspace/applyPatch.js +53 -0
- package/dist/tools/{discardWorktree.js → workspace/discardWorktree.js} +3 -3
- package/dist/tools/workspace/index.d.ts +10 -0
- package/dist/tools/workspace/index.js +10 -0
- package/dist/tools/workspace/listAgents.d.ts +23 -0
- package/dist/tools/workspace/listAgents.js +56 -0
- package/dist/tools/{listWorkspace.js → workspace/listWorkspace.js} +3 -3
- package/dist/tools/{mergeWorktree.js → workspace/mergeWorktree.js} +3 -3
- package/dist/tools/{readWorkspaceFile.js → workspace/readWorkspaceFile.js} +8 -8
- package/dist/tools/{recommendAgentForTask.js → workspace/recommendAgentForTask.js} +3 -3
- package/dist/tools/{searchWorkspace.js → workspace/searchWorkspace.js} +5 -5
- package/dist/tools/{syncFile.d.ts → workspace/syncFile.d.ts} +1 -1
- package/dist/tools/workspace/syncFile.js +134 -0
- package/dist/utils/atomicFile.d.ts +7 -0
- package/dist/utils/atomicFile.js +67 -0
- package/dist/utils/boundedFile.d.ts +8 -0
- package/dist/utils/boundedFile.js +93 -0
- package/dist/utils/lockedJsonFile.d.ts +15 -0
- package/dist/utils/lockedJsonFile.js +224 -0
- package/dist/utils/platform.d.ts +11 -0
- package/dist/utils/platform.js +26 -0
- package/dist/utils/stableJson.d.ts +2 -0
- package/dist/utils/stableJson.js +12 -0
- package/dist/version.d.ts +3 -2
- package/dist/version.js +3 -2
- package/dist/watcherStatus.d.ts +2 -2
- package/dist/watcherStatus.js +18 -0
- package/docs/CODE_WIKI.md +808 -143
- package/docs/architecture.md +15 -55
- package/docs/assets/patchwarden-desktop-onboarding.png +0 -0
- package/docs/control-center/README.md +4 -4
- package/docs/dashboard-overview.md +4 -4
- package/docs/desktop-app.md +175 -0
- package/docs/evidence-pack-schema.md +3 -3
- package/docs/open-source-application.md +16 -11
- package/docs/release-checklist.md +1 -1
- package/docs/release-evidence.md +35 -39
- package/docs/task-safe-review-workflow.md +5 -5
- package/docs/threat-model.md +19 -1
- package/examples/config.example.json +4 -2
- package/package.json +27 -10
- package/scripts/README.md +15 -6
- package/scripts/build.js +11 -0
- package/scripts/checks/brand-check.js +3 -0
- package/scripts/checks/build-output-check.js +37 -0
- package/scripts/checks/control-center-smoke.js +102 -10
- package/scripts/checks/control-smoke.js +19 -8
- package/scripts/checks/http-mcp-smoke.js +82 -4
- package/scripts/checks/lifecycle-smoke.js +15 -12
- package/scripts/checks/mcp-manifest-check.js +1 -1
- package/scripts/checks/mcp-smoke.js +58 -4
- package/scripts/checks/package-manifest-check.js +20 -7
- package/scripts/checks/unit-tests.js +3 -5
- package/scripts/checks/watcher-supervisor-smoke.js +38 -1
- package/scripts/control/get-patchwarden-health.ps1 +1 -1
- package/scripts/control/manage-patchwarden.ps1 +69 -4
- package/scripts/control/provision-patchwarden-tunnel.ps1 +127 -0
- package/scripts/control/run-background-supervisor.ps1 +41 -0
- package/scripts/control/start-patchwarden-tunnel.ps1 +191 -33
- package/scripts/e2e/demo-runtime.mjs +153 -0
- package/{PatchWarden-Control-Tray.cmd → scripts/launchers/PatchWarden-Control-Tray.cmd} +3 -3
- package/scripts/launchers/PatchWarden-Control.cmd +6 -0
- package/{PatchWarden-Desktop.cmd → scripts/launchers/PatchWarden-Desktop.cmd} +2 -2
- package/scripts/launchers/Restart-PatchWarden-Control.cmd +6 -0
- package/{Stop-PatchWarden.cmd → scripts/launchers/Stop-PatchWarden.cmd} +2 -2
- package/scripts/lib/clean-generated-output.js +11 -0
- package/scripts/lib/file-discovery.js +12 -0
- package/scripts/release/desktop-preflight.js +211 -0
- package/scripts/release/pack-clean.js +59 -14
- package/src/assessments/agentAssessor.ts +26 -12
- package/src/assessments/assessmentDiagnostics.ts +172 -0
- package/src/assessments/assessmentStore.ts +288 -51
- package/src/assessments/securitySnapshot.ts +231 -0
- package/src/config.ts +70 -13
- package/src/control/fileManager.ts +40 -0
- package/src/control/middleware/auth.ts +22 -4
- package/src/control/middleware/static.ts +16 -3
- package/src/control/routeTable.ts +301 -0
- package/src/control/routes/audit.ts +14 -6
- package/src/control/routes/evidence.ts +1 -1
- package/src/control/routes/lineage.ts +1 -1
- package/src/control/routes/process.ts +341 -40
- package/src/control/routes/sessions.ts +88 -25
- package/src/control/routes/status.ts +134 -17
- package/src/control/routes/taskActions.ts +24 -58
- package/src/control/routes/tasks.ts +66 -58
- package/src/control/routes/workspace.ts +56 -25
- package/src/control/runtime.ts +71 -43
- package/src/control/server.ts +37 -342
- package/src/control/shared.ts +77 -11
- package/src/diagnostics/allowedTestCommandSafety.ts +12 -0
- package/src/direct/directAudit.ts +17 -9
- package/src/direct/directGuards.ts +72 -44
- package/src/direct/directPatch.ts +95 -8
- package/src/direct/directSessionStore.ts +213 -55
- package/src/direct/directVerification.ts +9 -23
- package/src/doctor.ts +76 -64
- package/src/goal/acceptanceEngine.ts +1 -1
- package/src/goal/goalProgress.ts +17 -13
- package/src/goal/goalReport.ts +5 -13
- package/src/goal/goalStatus.ts +19 -3
- package/src/goal/goalStore.ts +241 -54
- package/src/goal/handoffExport.ts +6 -6
- package/src/goal/specKitImport.ts +12 -12
- package/src/goal/subgoalSync.ts +11 -8
- package/src/goal/worktreeManager.ts +139 -22
- package/src/httpServer.ts +113 -30
- package/src/logging.ts +10 -3
- package/src/policy/projectPolicy.ts +5 -2
- package/src/release/releaseGate.ts +74 -27
- package/src/runner/agentInvocation.ts +126 -4
- package/src/runner/changeCapture.ts +291 -57
- package/src/runner/postTaskCleanup.ts +41 -17
- package/src/runner/processSecurity.ts +433 -0
- package/src/runner/runTask.ts +197 -97
- package/src/runner/simpleProcess.ts +66 -31
- package/src/{taskProgress.ts → runner/taskProgress.ts} +3 -3
- package/src/{taskRuntime.ts → runner/taskRuntime.ts} +6 -3
- package/src/runner/taskStatusStore.ts +69 -0
- package/src/runner/watch.ts +348 -52
- package/src/security/contentRedaction.ts +2 -2
- package/src/security/discoveryTokenStore.ts +18 -4
- package/src/security/loopbackHost.ts +19 -0
- package/src/security/pathGuard.ts +35 -16
- package/src/security/planGuard.ts +2 -1
- package/src/security/runtimeGuard.ts +9 -6
- package/src/security/secretComparison.ts +13 -0
- package/src/security/sensitiveGuard.ts +27 -8
- package/src/security/toolInvocationGuard.ts +2 -2
- package/src/security/workspaceRootGuard.ts +53 -0
- package/src/smoke-test.ts +27 -26
- package/src/tools/catalog/index.ts +4 -0
- package/src/tools/{toolCatalog.ts → catalog/toolCatalog.ts} +3 -12
- package/src/tools/{toolRegistry.ts → catalog/toolRegistry.ts} +7 -16
- package/src/tools/definitions/toolDefs.ts +1264 -0
- package/src/tools/{auditSession.ts → diagnostics/auditSession.ts} +2 -2
- package/src/tools/{auditTask.ts → diagnostics/auditTask.ts} +136 -39
- package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +37 -9
- package/src/tools/diagnostics/index.ts +6 -0
- package/src/tools/{safeStatus.ts → diagnostics/safeStatus.ts} +7 -7
- package/src/tools/{safeViews.ts → diagnostics/safeViews.ts} +27 -17
- package/src/tools/{schemaDriftCheck.ts → diagnostics/schemaDriftCheck.ts} +3 -3
- package/src/tools/{createDirectSession.ts → direct/createDirectSession.ts} +6 -6
- package/src/tools/{finalizeDirectSession.ts → direct/finalizeDirectSession.ts} +7 -5
- package/src/tools/direct/index.ts +3 -0
- package/src/tools/{runDirectVerificationBundle.ts → direct/runDirectVerificationBundle.ts} +2 -2
- package/src/tools/{discoverTools.ts → discovery/discoverTools.ts} +6 -6
- package/src/tools/{explainTool.ts → discovery/explainTool.ts} +3 -3
- package/src/tools/discovery/index.ts +3 -0
- package/src/tools/{invokeDiscoveredTool.ts → discovery/invokeDiscoveredTool.ts} +7 -7
- package/src/tools/dispatch/coreDispatch.ts +36 -31
- package/src/tools/dispatch/diagnosticDispatch.ts +26 -21
- package/src/tools/dispatch/directDispatch.ts +11 -10
- package/src/tools/dispatch/goalDispatch.ts +5 -4
- package/src/tools/dispatch/releaseDispatch.ts +3 -2
- package/src/tools/dispatch/validation.ts +78 -0
- package/src/tools/{getPlan.ts → goals/getPlan.ts} +4 -4
- package/src/tools/{goalSubgoalTask.ts → goals/goalSubgoalTask.ts} +41 -26
- package/src/tools/goals/index.ts +3 -0
- package/src/tools/{savePlan.ts → goals/savePlan.ts} +38 -8
- package/src/tools/registry.ts +17 -1261
- package/src/tools/{checkReleaseGate.ts → release/checkReleaseGate.ts} +3 -3
- package/src/tools/release/index.ts +2 -0
- package/src/tools/{releaseMode.ts → release/releaseMode.ts} +42 -24
- package/src/tools/tasks/cancelTask.ts +110 -0
- package/src/tools/{createTask.ts → tasks/createTask.ts} +161 -33
- package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +12 -16
- package/src/tools/{evidencePack.ts → tasks/evidencePack.ts} +36 -22
- package/src/tools/{getTaskFile.ts → tasks/getTaskFile.ts} +4 -4
- package/src/tools/{getTaskStatus.ts → tasks/getTaskStatus.ts} +13 -6
- package/src/tools/{getTaskStdoutTail.ts → tasks/getTaskStdoutTail.ts} +13 -11
- package/src/tools/{getTaskSummary.ts → tasks/getTaskSummary.ts} +46 -44
- package/src/tools/tasks/index.ts +18 -0
- package/src/tools/{listTasks.ts → tasks/listTasks.ts} +6 -5
- package/src/tools/{reconcileTasks.ts → tasks/reconcileTasks.ts} +228 -77
- package/src/tools/tasks/retryTask.ts +69 -0
- package/src/tools/{runTaskLoop.ts → tasks/runTaskLoop.ts} +73 -46
- package/src/tools/{runVerification.ts → tasks/runVerification.ts} +6 -3
- package/src/tools/{taskLineage.ts → tasks/taskLineage.ts} +8 -7
- package/src/tools/{taskOutputs.ts → tasks/taskOutputs.ts} +27 -20
- package/src/tools/tasks/taskStates.ts +54 -0
- package/src/tools/{waitForTask.ts → tasks/waitForTask.ts} +3 -12
- package/src/tools/{androidDoctor.ts → workspace/androidDoctor.ts} +15 -10
- package/src/tools/{applyPatch.ts → workspace/applyPatch.ts} +38 -16
- package/src/tools/{discardWorktree.ts → workspace/discardWorktree.ts} +3 -3
- package/src/tools/workspace/index.ts +10 -0
- package/src/tools/workspace/listAgents.ts +75 -0
- package/src/tools/{listWorkspace.ts → workspace/listWorkspace.ts} +3 -3
- package/src/tools/{mergeWorktree.ts → workspace/mergeWorktree.ts} +3 -3
- package/src/tools/{readWorkspaceFile.ts → workspace/readWorkspaceFile.ts} +8 -8
- package/src/tools/{recommendAgentForTask.ts → workspace/recommendAgentForTask.ts} +3 -3
- package/src/tools/{searchWorkspace.ts → workspace/searchWorkspace.ts} +5 -5
- package/src/tools/workspace/syncFile.ts +292 -0
- package/src/utils/atomicFile.ts +74 -0
- package/src/utils/boundedFile.ts +116 -0
- package/src/utils/lockedJsonFile.ts +251 -0
- package/src/utils/platform.ts +31 -0
- package/src/utils/stableJson.ts +12 -0
- package/src/version.ts +3 -2
- package/src/watcherStatus.ts +19 -1
- package/ui/desktop-bootstrap.js +11 -0
- package/ui/desktop-bridge.js +36 -0
- package/ui/desktop.css +160 -0
- package/ui/getting-started.js +93 -0
- package/ui/i18n.js +484 -0
- package/ui/log-parser.js +30 -0
- package/ui/pages/audit.html +12 -0
- package/ui/pages/dashboard.html +94 -147
- package/ui/pages/direct-sessions.html +123 -26
- package/ui/pages/getting-started.html +43 -0
- package/ui/pages/logs.html +134 -22
- package/ui/pages/settings.html +101 -0
- package/ui/pages/task-detail.html +12 -0
- package/ui/pages/tasks.html +54 -8
- package/ui/pages/workspace.html +38 -1
- package/ui/settings.js +254 -0
- package/PatchWarden-Control.cmd +0 -6
- package/Restart-PatchWarden-Control.cmd +0 -6
- package/dist/test/unit/acceptance-engine.test.d.ts +0 -1
- package/dist/test/unit/acceptance-engine.test.js +0 -228
- package/dist/test/unit/agent-router.test.d.ts +0 -1
- package/dist/test/unit/agent-router.test.js +0 -287
- package/dist/test/unit/android-doctor.test.d.ts +0 -1
- package/dist/test/unit/android-doctor.test.js +0 -118
- package/dist/test/unit/apply-patch.test.d.ts +0 -1
- package/dist/test/unit/apply-patch.test.js +0 -225
- package/dist/test/unit/audit-checks.test.d.ts +0 -1
- package/dist/test/unit/audit-checks.test.js +0 -350
- package/dist/test/unit/chinese-path.test.d.ts +0 -1
- package/dist/test/unit/chinese-path.test.js +0 -91
- package/dist/test/unit/command-guard.test.d.ts +0 -1
- package/dist/test/unit/command-guard.test.js +0 -160
- package/dist/test/unit/create-task.test.d.ts +0 -1
- package/dist/test/unit/create-task.test.js +0 -197
- package/dist/test/unit/diagnose-task.test.d.ts +0 -1
- package/dist/test/unit/diagnose-task.test.js +0 -457
- package/dist/test/unit/direct-guards.test.d.ts +0 -1
- package/dist/test/unit/direct-guards.test.js +0 -328
- package/dist/test/unit/discovery-token-store.test.d.ts +0 -1
- package/dist/test/unit/discovery-token-store.test.js +0 -139
- package/dist/test/unit/evidence-pack.test.d.ts +0 -1
- package/dist/test/unit/evidence-pack.test.js +0 -224
- package/dist/test/unit/get-task-status.test.d.ts +0 -1
- package/dist/test/unit/get-task-status.test.js +0 -174
- package/dist/test/unit/get-task-summary.test.d.ts +0 -1
- package/dist/test/unit/get-task-summary.test.js +0 -146
- package/dist/test/unit/goal-graph.test.d.ts +0 -1
- package/dist/test/unit/goal-graph.test.js +0 -298
- package/dist/test/unit/goal-progress.test.d.ts +0 -1
- package/dist/test/unit/goal-progress.test.js +0 -381
- package/dist/test/unit/goal-report.test.d.ts +0 -1
- package/dist/test/unit/goal-report.test.js +0 -159
- package/dist/test/unit/goal-status.test.d.ts +0 -1
- package/dist/test/unit/goal-status.test.js +0 -215
- package/dist/test/unit/goal-store.test.d.ts +0 -1
- package/dist/test/unit/goal-store.test.js +0 -253
- package/dist/test/unit/goal-subgoal-task.test.d.ts +0 -1
- package/dist/test/unit/goal-subgoal-task.test.js +0 -55
- package/dist/test/unit/goal-tools-registry.test.d.ts +0 -1
- package/dist/test/unit/goal-tools-registry.test.js +0 -192
- package/dist/test/unit/handoff-export.test.d.ts +0 -1
- package/dist/test/unit/handoff-export.test.js +0 -263
- package/dist/test/unit/invoke-discovered-tool.test.d.ts +0 -1
- package/dist/test/unit/invoke-discovered-tool.test.js +0 -167
- package/dist/test/unit/logging.test.d.ts +0 -1
- package/dist/test/unit/logging.test.js +0 -397
- package/dist/test/unit/path-guard.test.d.ts +0 -1
- package/dist/test/unit/path-guard.test.js +0 -133
- package/dist/test/unit/post-task-cleanup.test.d.ts +0 -1
- package/dist/test/unit/post-task-cleanup.test.js +0 -48
- package/dist/test/unit/project-policy-release-mode.test.d.ts +0 -1
- package/dist/test/unit/project-policy-release-mode.test.js +0 -125
- package/dist/test/unit/reconcile-tasks.test.d.ts +0 -1
- package/dist/test/unit/reconcile-tasks.test.js +0 -456
- package/dist/test/unit/release-gate.test.d.ts +0 -1
- package/dist/test/unit/release-gate.test.js +0 -242
- package/dist/test/unit/run-task-loop.test.d.ts +0 -1
- package/dist/test/unit/run-task-loop.test.js +0 -380
- package/dist/test/unit/safe-status.test.d.ts +0 -1
- package/dist/test/unit/safe-status.test.js +0 -165
- package/dist/test/unit/safe-views.test.d.ts +0 -1
- package/dist/test/unit/safe-views.test.js +0 -171
- package/dist/test/unit/schema-drift-check.test.d.ts +0 -1
- package/dist/test/unit/schema-drift-check.test.js +0 -176
- package/dist/test/unit/sensitive-guard.test.d.ts +0 -1
- package/dist/test/unit/sensitive-guard.test.js +0 -104
- package/dist/test/unit/spec-kit-import.test.d.ts +0 -1
- package/dist/test/unit/spec-kit-import.test.js +0 -341
- package/dist/test/unit/subgoal-sync.test.d.ts +0 -1
- package/dist/test/unit/subgoal-sync.test.js +0 -183
- package/dist/test/unit/sync-file.test.d.ts +0 -1
- package/dist/test/unit/sync-file.test.js +0 -154
- package/dist/test/unit/tool-invocation-guard.test.d.ts +0 -1
- package/dist/test/unit/tool-invocation-guard.test.js +0 -432
- package/dist/test/unit/tool-usage-stats.test.d.ts +0 -1
- package/dist/test/unit/tool-usage-stats.test.js +0 -300
- package/dist/test/unit/toolSearch.test.d.ts +0 -1
- package/dist/test/unit/toolSearch.test.js +0 -571
- package/dist/test/unit/wait-for-task.test.d.ts +0 -1
- package/dist/test/unit/wait-for-task.test.js +0 -144
- package/dist/test/unit/watcher-status.test.d.ts +0 -1
- package/dist/test/unit/watcher-status.test.js +0 -169
- package/dist/test/unit/worktree-manager.test.d.ts +0 -1
- package/dist/test/unit/worktree-manager.test.js +0 -176
- package/dist/tools/applyPatch.js +0 -41
- package/dist/tools/cancelTask.d.ts +0 -30
- package/dist/tools/cancelTask.js +0 -64
- package/dist/tools/finalizeDirectSession.js +0 -84
- package/dist/tools/goalSubgoalTask.js +0 -110
- package/dist/tools/killTask.d.ts +0 -15
- package/dist/tools/listAgents.d.ts +0 -12
- package/dist/tools/listAgents.js +0 -42
- package/dist/tools/reconcileTasks.js +0 -352
- package/dist/tools/retryTask.js +0 -45
- package/dist/tools/runVerification.js +0 -36
- package/dist/tools/syncFile.js +0 -65
- package/docs/control-center/control-center-daily-driver.md +0 -211
- package/docs/control-center/control-center-mvp.md +0 -205
- package/docs/control-center/control-center-phase2.md +0 -159
- package/docs/execution-plan-2026-07-09.md +0 -315
- package/docs/release-v0.6.0.md +0 -71
- package/docs/release-v0.6.1.md +0 -75
- package/docs/release-v0.6.4.md +0 -45
- package/docs/roadmap-execution-and-acceptance.md +0 -365
- package/src/test/unit/acceptance-engine.test.ts +0 -261
- package/src/test/unit/agent-router.test.ts +0 -342
- package/src/test/unit/android-doctor.test.ts +0 -158
- package/src/test/unit/apply-patch.test.ts +0 -293
- package/src/test/unit/audit-checks.test.ts +0 -567
- package/src/test/unit/chinese-path.test.ts +0 -106
- package/src/test/unit/command-guard.test.ts +0 -221
- package/src/test/unit/create-task.test.ts +0 -255
- package/src/test/unit/diagnose-task.test.ts +0 -544
- package/src/test/unit/direct-guards.test.ts +0 -467
- package/src/test/unit/discovery-token-store.test.ts +0 -181
- package/src/test/unit/evidence-pack.test.ts +0 -251
- package/src/test/unit/get-task-status.test.ts +0 -203
- package/src/test/unit/get-task-summary.test.ts +0 -173
- package/src/test/unit/goal-graph.test.ts +0 -347
- package/src/test/unit/goal-progress.test.ts +0 -538
- package/src/test/unit/goal-report.test.ts +0 -189
- package/src/test/unit/goal-status.test.ts +0 -270
- package/src/test/unit/goal-store.test.ts +0 -318
- package/src/test/unit/goal-subgoal-task.test.ts +0 -72
- package/src/test/unit/goal-tools-registry.test.ts +0 -245
- package/src/test/unit/handoff-export.test.ts +0 -295
- package/src/test/unit/invoke-discovered-tool.test.ts +0 -216
- package/src/test/unit/logging.test.ts +0 -497
- package/src/test/unit/path-guard.test.ts +0 -180
- package/src/test/unit/post-task-cleanup.test.ts +0 -53
- package/src/test/unit/project-policy-release-mode.test.ts +0 -156
- package/src/test/unit/reconcile-tasks.test.ts +0 -551
- package/src/test/unit/release-gate.test.ts +0 -314
- package/src/test/unit/run-task-loop.test.ts +0 -425
- package/src/test/unit/safe-status.test.ts +0 -187
- package/src/test/unit/safe-views.test.ts +0 -184
- package/src/test/unit/schema-drift-check.test.ts +0 -260
- package/src/test/unit/sensitive-guard.test.ts +0 -124
- package/src/test/unit/spec-kit-import.test.ts +0 -429
- package/src/test/unit/subgoal-sync.test.ts +0 -236
- package/src/test/unit/sync-file.test.ts +0 -231
- package/src/test/unit/tool-invocation-guard.test.ts +0 -542
- package/src/test/unit/tool-usage-stats.test.ts +0 -384
- package/src/test/unit/toolSearch.test.ts +0 -756
- package/src/test/unit/wait-for-task.test.ts +0 -176
- package/src/test/unit/watcher-status.test.ts +0 -190
- package/src/test/unit/worktree-manager.test.ts +0 -247
- package/src/tools/cancelTask.ts +0 -70
- package/src/tools/listAgents.ts +0 -54
- package/src/tools/retryTask.ts +0 -53
- package/src/tools/syncFile.ts +0 -122
- /package/dist/tools/{toolCatalog.d.ts → catalog/toolCatalog.d.ts} +0 -0
- /package/dist/tools/{toolSearch.d.ts → catalog/toolSearch.d.ts} +0 -0
- /package/dist/tools/{toolSearch.js → catalog/toolSearch.js} +0 -0
- /package/dist/tools/{toolUsageStats.d.ts → catalog/toolUsageStats.d.ts} +0 -0
- /package/dist/tools/{toolUsageStats.js → catalog/toolUsageStats.js} +0 -0
- /package/dist/tools/{createDirectSession.d.ts → direct/createDirectSession.d.ts} +0 -0
- /package/dist/tools/{runDirectVerificationBundle.d.ts → direct/runDirectVerificationBundle.d.ts} +0 -0
- /package/dist/tools/{getPlan.d.ts → goals/getPlan.d.ts} +0 -0
- /package/dist/tools/{savePlan.d.ts → goals/savePlan.d.ts} +0 -0
- /package/dist/tools/{evidencePack.d.ts → tasks/evidencePack.d.ts} +0 -0
- /package/dist/tools/{getTaskFile.d.ts → tasks/getTaskFile.d.ts} +0 -0
- /package/dist/tools/{getTaskProgress.d.ts → tasks/getTaskProgress.d.ts} +0 -0
- /package/dist/tools/{getTaskProgress.js → tasks/getTaskProgress.js} +0 -0
- /package/dist/tools/{getTaskStdoutTail.d.ts → tasks/getTaskStdoutTail.d.ts} +0 -0
- /package/dist/tools/{getTaskSummary.d.ts → tasks/getTaskSummary.d.ts} +0 -0
- /package/dist/tools/{killTask.js → tasks/killTask.js} +0 -0
- /package/dist/tools/{retryTask.d.ts → tasks/retryTask.d.ts} +0 -0
- /package/dist/tools/{runVerification.d.ts → tasks/runVerification.d.ts} +0 -0
- /package/dist/tools/{taskLineage.d.ts → tasks/taskLineage.d.ts} +0 -0
- /package/dist/tools/{waitForTask.d.ts → tasks/waitForTask.d.ts} +0 -0
- /package/dist/tools/{androidDoctor.d.ts → workspace/androidDoctor.d.ts} +0 -0
- /package/dist/tools/{discardWorktree.d.ts → workspace/discardWorktree.d.ts} +0 -0
- /package/dist/tools/{listWorkspace.d.ts → workspace/listWorkspace.d.ts} +0 -0
- /package/dist/tools/{mergeWorktree.d.ts → workspace/mergeWorktree.d.ts} +0 -0
- /package/dist/tools/{readWorkspaceFile.d.ts → workspace/readWorkspaceFile.d.ts} +0 -0
- /package/dist/tools/{recommendAgentForTask.d.ts → workspace/recommendAgentForTask.d.ts} +0 -0
- /package/dist/tools/{searchWorkspace.d.ts → workspace/searchWorkspace.d.ts} +0 -0
- /package/src/tools/{toolSearch.ts → catalog/toolSearch.ts} +0 -0
- /package/src/tools/{toolUsageStats.ts → catalog/toolUsageStats.ts} +0 -0
- /package/src/tools/{getTaskProgress.ts → tasks/getTaskProgress.ts} +0 -0
- /package/src/tools/{killTask.ts → tasks/killTask.ts} +0 -0
package/dist/runner/runTask.js
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
-
import {
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { existsSync, readFileSync, } from "node:fs";
|
|
3
4
|
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
4
5
|
import { getTasksDir, getPlansDir, getConfig, resolveWorkspaceRoot } from "../config.js";
|
|
5
6
|
import { guardPath, guardWorkspacePath } from "../security/pathGuard.js";
|
|
6
7
|
import { guardTestCommand, } from "../security/commandGuard.js";
|
|
7
|
-
import { writeTaskProgress } from "
|
|
8
|
-
import { writeTaskRuntime } from "
|
|
8
|
+
import { writeTaskProgress } from "./taskProgress.js";
|
|
9
|
+
import { writeTaskRuntime } from "./taskRuntime.js";
|
|
10
|
+
import { claimPendingTask, readTaskStatusFile, updateTaskStatusFile, } from "./taskStatusStore.js";
|
|
9
11
|
import { validateAssessmentFreshness } from "../assessments/assessmentStore.js";
|
|
12
|
+
import { recordAssessmentValidationFailure } from "../assessments/assessmentDiagnostics.js";
|
|
10
13
|
import { buildAgentInvocation, buildExecutionPrompt } from "./agentInvocation.js";
|
|
11
14
|
import { buildChangeArtifacts, captureRepoSnapshot, compareSnapshots, emptyArtifactHygiene, writeSnapshot, extractExternalDirtyFiles, findNewExternalDirtyFiles, buildArtifactManifest, groupChangedFiles, } from "./changeCapture.js";
|
|
12
15
|
import { errorPayload } from "../errors.js";
|
|
13
|
-
import {
|
|
16
|
+
import { ARTIFACT_SCHEMA_VERSION } from "../version.js";
|
|
17
|
+
import { diagnoseAndroidBuild } from "../tools/workspace/androidDoctor.js";
|
|
14
18
|
import { runPostTaskCleanup } from "./postTaskCleanup.js";
|
|
19
|
+
import { atomicWriteFileSync, atomicWriteJsonFileSync } from "../utils/atomicFile.js";
|
|
20
|
+
import { allowedEnvironmentValues, buildChildEnvironment, redactProcessOutput, resolvePackageManagerInvocation, resolveTrustedExecutable, SecureProcessLogCapture, } from "./processSecurity.js";
|
|
15
21
|
const HEARTBEAT_INTERVAL_MS = 2000;
|
|
16
22
|
const GRACEFUL_KILL_MS = 2000;
|
|
17
23
|
const MAX_CAPTURE_CHARS = 100_000;
|
|
@@ -36,7 +42,25 @@ async function prepareTask(taskId) {
|
|
|
36
42
|
const statusFile = join(taskDir, "status.json");
|
|
37
43
|
if (!existsSync(statusFile))
|
|
38
44
|
throw new Error(`Task not found: "${taskId}"`);
|
|
39
|
-
const
|
|
45
|
+
const startedAtMs = Date.now();
|
|
46
|
+
const runnerInstanceId = randomUUID().replace(/-/g, "");
|
|
47
|
+
const claim = claimPendingTask(statusFile, {
|
|
48
|
+
status: "running",
|
|
49
|
+
phase: "preparing",
|
|
50
|
+
started_at: new Date(startedAtMs).toISOString(),
|
|
51
|
+
last_heartbeat_at: new Date(startedAtMs).toISOString(),
|
|
52
|
+
current_command: null,
|
|
53
|
+
error: null,
|
|
54
|
+
});
|
|
55
|
+
if (!claim.claimed) {
|
|
56
|
+
const currentStatus = String(claim.status.status || "failed");
|
|
57
|
+
return {
|
|
58
|
+
task_id: taskId,
|
|
59
|
+
status: currentStatus,
|
|
60
|
+
error: `Task is ${currentStatus}; only pending tasks can be claimed for execution.`,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const initialStatus = claim.status;
|
|
40
64
|
const planId = String(initialStatus.plan_id || "");
|
|
41
65
|
const agentName = String(initialStatus.agent || "");
|
|
42
66
|
const rawRepoPath = String(initialStatus.resolved_repo_path || initialStatus.repo_path || wsRoot);
|
|
@@ -50,7 +74,6 @@ async function prepareTask(taskId) {
|
|
|
50
74
|
catch (error) {
|
|
51
75
|
return failBeforeExecution(taskId, taskDir, errorMessage(error), error);
|
|
52
76
|
}
|
|
53
|
-
const startedAtMs = Date.now();
|
|
54
77
|
const deadlineMs = startedAtMs + timeoutSeconds * 1000;
|
|
55
78
|
let repoPath;
|
|
56
79
|
try {
|
|
@@ -73,7 +96,11 @@ async function prepareTask(taskId) {
|
|
|
73
96
|
const preExecSnapshot = await captureRepoSnapshot(repoPath);
|
|
74
97
|
const validation = validateAssessmentFreshness(assessmentId, preExecSnapshot);
|
|
75
98
|
if (!validation.valid) {
|
|
76
|
-
|
|
99
|
+
recordAssessmentValidationFailure(validation);
|
|
100
|
+
const changedFields = validation.config_change_categories?.length
|
|
101
|
+
? ` Changed fields: ${validation.config_change_categories.join(", ")}.`
|
|
102
|
+
: "";
|
|
103
|
+
const message = `assessment validation failed: ${validation.failure_reason}.${changedFields} Re-run create_task with execution_mode=assess_only to get a fresh assessment_id.`;
|
|
77
104
|
return failBeforeExecution(taskId, taskDir, message);
|
|
78
105
|
}
|
|
79
106
|
}
|
|
@@ -82,18 +109,12 @@ async function prepareTask(taskId) {
|
|
|
82
109
|
return failBeforeExecution(taskId, taskDir, message, error);
|
|
83
110
|
}
|
|
84
111
|
}
|
|
85
|
-
updateStatus(taskDir, {
|
|
86
|
-
status: "running",
|
|
87
|
-
phase: "preparing",
|
|
88
|
-
started_at: new Date(startedAtMs).toISOString(),
|
|
89
|
-
last_heartbeat_at: new Date().toISOString(),
|
|
90
|
-
timeout_seconds: timeoutSeconds,
|
|
91
|
-
current_command: null,
|
|
92
|
-
error: null,
|
|
93
|
-
});
|
|
112
|
+
updateStatus(taskDir, { timeout_seconds: timeoutSeconds });
|
|
94
113
|
writeTaskRuntime(taskDir, {
|
|
95
114
|
task_started_at: new Date(startedAtMs).toISOString(),
|
|
96
115
|
watcher_instance_id: process.env.PATCHWARDEN_WATCHER_INSTANCE_ID || undefined,
|
|
116
|
+
runner_pid: process.pid,
|
|
117
|
+
runner_instance_id: runnerInstanceId,
|
|
97
118
|
});
|
|
98
119
|
setTaskPhase(taskDir, "preparing", null, "Capturing pre-task repository state.");
|
|
99
120
|
let beforeSnapshot;
|
|
@@ -113,12 +134,13 @@ async function prepareTask(taskId) {
|
|
|
113
134
|
return {
|
|
114
135
|
taskId, taskDir, statusFile, repoPath, wsRoot, config, plansDir, initialStatus,
|
|
115
136
|
planId, agentName, testCommand, changePolicy, verifyCommands, timeoutSeconds,
|
|
116
|
-
startedAtMs, deadlineMs, beforeSnapshot, beforeWorkspaceSnapshot, externalDirtyBaseline,
|
|
137
|
+
startedAtMs, deadlineMs, runnerInstanceId, beforeSnapshot, beforeWorkspaceSnapshot, externalDirtyBaseline,
|
|
117
138
|
};
|
|
118
139
|
}
|
|
119
140
|
async function executeAgent(ctx) {
|
|
120
141
|
const state = {
|
|
121
142
|
agentResult: null,
|
|
143
|
+
agentFailureCategory: null,
|
|
122
144
|
testResult: skippedTest(ctx.testCommand, ctx.repoPath, "Agent did not complete successfully."),
|
|
123
145
|
verifyResults: [],
|
|
124
146
|
finalStatus: "failed",
|
|
@@ -142,8 +164,11 @@ async function executeAgent(ctx) {
|
|
|
142
164
|
phase: "executing_agent",
|
|
143
165
|
currentCommand: invocation.commandLabel,
|
|
144
166
|
deadlineMs: ctx.deadlineMs,
|
|
167
|
+
runnerInstanceId: ctx.runnerInstanceId,
|
|
145
168
|
stdoutPath: join(ctx.taskDir, "stdout.log"),
|
|
146
169
|
stderrPath: join(ctx.taskDir, "stderr.log"),
|
|
170
|
+
environmentVariableNames: invocation.environmentVariableNames,
|
|
171
|
+
blockedEnvironmentVariableNames: invocation.blockedEnvironmentVariableNames,
|
|
147
172
|
});
|
|
148
173
|
const agentResult = state.agentResult;
|
|
149
174
|
if (agentResult.terminationReason === "canceled" || agentResult.terminationReason === "killed") {
|
|
@@ -153,13 +178,17 @@ async function executeAgent(ctx) {
|
|
|
153
178
|
: "Task was canceled by user request.";
|
|
154
179
|
}
|
|
155
180
|
else if (agentResult.terminationReason === "timeout") {
|
|
181
|
+
state.finalStatus = "timeout";
|
|
156
182
|
state.finalError = `Task timed out after ${ctx.timeoutSeconds} seconds during agent execution.`;
|
|
157
183
|
}
|
|
158
184
|
else if (agentResult.spawnError) {
|
|
159
185
|
state.finalError = `Agent spawn failed: ${agentResult.spawnError}`;
|
|
160
186
|
}
|
|
161
187
|
else if (agentResult.exitCode !== 0) {
|
|
162
|
-
state.
|
|
188
|
+
state.agentFailureCategory = classifyAgentFailure(agentResult.stderr);
|
|
189
|
+
state.finalError = state.agentFailureCategory
|
|
190
|
+
? `Agent provider failure (${state.agentFailureCategory}); process exited with code ${agentResult.exitCode}.`
|
|
191
|
+
: `Agent exited with code ${agentResult.exitCode}.`;
|
|
163
192
|
}
|
|
164
193
|
}
|
|
165
194
|
catch (error) {
|
|
@@ -175,7 +204,7 @@ async function runVerification(ctx, state) {
|
|
|
175
204
|
if (ctx.verifyCommands.length > 0) {
|
|
176
205
|
for (const command of ctx.verifyCommands) {
|
|
177
206
|
setTaskPhase(ctx.taskDir, "running_tests", command);
|
|
178
|
-
const verification = await runTrustedTestCommand(command, ctx.repoPath, ctx.taskDir, ctx.statusFile, ctx.deadlineMs);
|
|
207
|
+
const verification = await runTrustedTestCommand(command, ctx.repoPath, ctx.taskDir, ctx.statusFile, ctx.deadlineMs, ctx.runnerInstanceId);
|
|
179
208
|
state.verifyResults.push(verification);
|
|
180
209
|
if (verification.terminationReason || Date.now() >= ctx.deadlineMs)
|
|
181
210
|
break;
|
|
@@ -190,6 +219,7 @@ async function runVerification(ctx, state) {
|
|
|
190
219
|
: "Task was canceled during verification.";
|
|
191
220
|
}
|
|
192
221
|
else if (interrupted?.terminationReason === "timeout" || Date.now() >= ctx.deadlineMs) {
|
|
222
|
+
state.finalStatus = "timeout";
|
|
193
223
|
state.finalError = `Task timed out after ${ctx.timeoutSeconds} seconds during verification.`;
|
|
194
224
|
}
|
|
195
225
|
else if (failedVerification) {
|
|
@@ -229,7 +259,7 @@ async function collectArtifacts(ctx, state) {
|
|
|
229
259
|
skipped: [{ path: ".", reason: "post_task_cleanup", skip_reason: errorMessage(error) }],
|
|
230
260
|
source_files_touched: 0,
|
|
231
261
|
};
|
|
232
|
-
|
|
262
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "post-task-cleanup.json"), cleanupReport);
|
|
233
263
|
updateStatus(ctx.taskDir, { cleanup: cleanupReport });
|
|
234
264
|
}
|
|
235
265
|
}
|
|
@@ -239,24 +269,16 @@ async function collectArtifacts(ctx, state) {
|
|
|
239
269
|
let artifactStatus = "collected";
|
|
240
270
|
let artifactCollectionError = null;
|
|
241
271
|
let timeoutHandle;
|
|
272
|
+
const artifactController = new AbortController();
|
|
242
273
|
try {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
new Promise((_, reject) => {
|
|
250
|
-
timeoutHandle = setTimeout(() => reject(new Error("Artifact collection timed out")), ARTIFACT_COLLECTION_TIMEOUT_MS);
|
|
251
|
-
}),
|
|
252
|
-
]);
|
|
253
|
-
if (timeoutHandle)
|
|
254
|
-
clearTimeout(timeoutHandle);
|
|
255
|
-
changes = collectionResult;
|
|
274
|
+
timeoutHandle = setTimeout(() => {
|
|
275
|
+
artifactController.abort(new Error("Artifact collection timed out"));
|
|
276
|
+
}, ARTIFACT_COLLECTION_TIMEOUT_MS);
|
|
277
|
+
const afterSnapshot = await captureRepoSnapshot(ctx.repoPath, artifactController.signal);
|
|
278
|
+
writeSnapshot(ctx.taskDir, "git-after.json", afterSnapshot);
|
|
279
|
+
changes = await buildChangeArtifacts(ctx.repoPath, ctx.beforeSnapshot, afterSnapshot, artifactController.signal);
|
|
256
280
|
}
|
|
257
281
|
catch (error) {
|
|
258
|
-
if (timeoutHandle)
|
|
259
|
-
clearTimeout(timeoutHandle);
|
|
260
282
|
state.lastCaughtError = error;
|
|
261
283
|
artifactCollectionError = errorMessage(error);
|
|
262
284
|
artifactStatus = artifactCollectionError.includes("timed out") ? "timeout" : "failed";
|
|
@@ -275,7 +297,7 @@ async function collectArtifacts(ctx, state) {
|
|
|
275
297
|
unavailable_reason: `Change capture failed: ${artifactCollectionError}`,
|
|
276
298
|
artifact_hygiene: emptyArtifactHygiene(),
|
|
277
299
|
};
|
|
278
|
-
|
|
300
|
+
atomicWriteFileSync(join(ctx.taskDir, "partial_result.md"), [
|
|
279
301
|
"# PatchWarden Partial Result",
|
|
280
302
|
"",
|
|
281
303
|
`Task: ${ctx.taskId}`,
|
|
@@ -295,7 +317,7 @@ async function collectArtifacts(ctx, state) {
|
|
|
295
317
|
"## Note",
|
|
296
318
|
"Artifact collection did not complete. The task result may be incomplete. Review stdout.log, stderr.log, and verify.json for details.",
|
|
297
319
|
"",
|
|
298
|
-
].join("\n")
|
|
320
|
+
].join("\n"));
|
|
299
321
|
if (state.finalStatus === "done_by_agent") {
|
|
300
322
|
state.finalError = `Change capture failed: ${artifactCollectionError}`;
|
|
301
323
|
state.finalStatus = "failed";
|
|
@@ -304,6 +326,10 @@ async function collectArtifacts(ctx, state) {
|
|
|
304
326
|
state.finalError ||= `Change capture failed: ${artifactCollectionError}`;
|
|
305
327
|
}
|
|
306
328
|
}
|
|
329
|
+
finally {
|
|
330
|
+
if (timeoutHandle)
|
|
331
|
+
clearTimeout(timeoutHandle);
|
|
332
|
+
}
|
|
307
333
|
const artifactCollectionFinishedAt = new Date().toISOString();
|
|
308
334
|
let outOfScopeChanges = [];
|
|
309
335
|
const preexistingExternalDirty = ctx.externalDirtyBaseline;
|
|
@@ -326,18 +352,18 @@ async function collectArtifacts(ctx, state) {
|
|
|
326
352
|
? [`Pre-existing external dirty files (not caused by this task): ${preexistingExternalDirty.length} file(s)`]
|
|
327
353
|
: [];
|
|
328
354
|
applyScopeViolationVerdict(ctx, state, changes, newOutOfScopeChanges, preexistingExternalDirty);
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
355
|
+
atomicWriteFileSync(join(ctx.taskDir, "git.diff"), changes.diff);
|
|
356
|
+
atomicWriteFileSync(join(ctx.taskDir, "diff.patch"), changes.diff);
|
|
357
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "changed-files.json"), { schema_version: ARTIFACT_SCHEMA_VERSION, ...changes });
|
|
332
358
|
const artifactManifest = await buildArtifactManifest(changes.changed_files, ctx.repoPath, ctx.taskId);
|
|
333
|
-
|
|
359
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "artifact_manifest.json"), artifactManifest);
|
|
334
360
|
const changedFileGroups = groupChangedFiles(changes.changed_files);
|
|
335
|
-
|
|
361
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "file-stats.json"), {
|
|
336
362
|
task_id: ctx.taskId,
|
|
337
363
|
additions: changes.additions,
|
|
338
364
|
deletions: changes.deletions,
|
|
339
365
|
files: changes.file_stats,
|
|
340
|
-
}
|
|
366
|
+
});
|
|
341
367
|
return {
|
|
342
368
|
changes, artifactStatus, artifactCollectionError, artifactCollectionStartedAt, artifactCollectionFinishedAt,
|
|
343
369
|
outOfScopeChanges, newOutOfScopeChanges, preexistingExternalDirty, preexistingWarnings, cleanupReport,
|
|
@@ -348,15 +374,15 @@ function applyScopeViolationVerdict(ctx, state, changes, newOutOfScopeChanges, p
|
|
|
348
374
|
if (newOutOfScopeChanges.length > 0) {
|
|
349
375
|
state.finalStatus = "failed_scope_violation";
|
|
350
376
|
state.finalError = `Detected ${newOutOfScopeChanges.length} new change(s) outside resolved_repo_path during task execution.`;
|
|
351
|
-
|
|
377
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "rollback-plan.json"), {
|
|
352
378
|
task_id: ctx.taskId,
|
|
353
379
|
status: "review_required",
|
|
354
380
|
automatic_rollback_performed: false,
|
|
355
381
|
warning: "Review ownership and concurrent edits before rollback. PatchWarden did not modify or restore these files.",
|
|
356
382
|
out_of_scope_changes: newOutOfScopeChanges,
|
|
357
383
|
preexisting_external_dirty_files: preexistingExternalDirty,
|
|
358
|
-
}
|
|
359
|
-
|
|
384
|
+
});
|
|
385
|
+
atomicWriteFileSync(join(ctx.taskDir, "rollback_scope_violation_plan.md"), [
|
|
360
386
|
"# Scope Violation Rollback Plan",
|
|
361
387
|
"",
|
|
362
388
|
`Task: ${ctx.taskId}`,
|
|
@@ -366,7 +392,11 @@ function applyScopeViolationVerdict(ctx, state, changes, newOutOfScopeChanges, p
|
|
|
366
392
|
"## New out-of-scope files (caused by this task)",
|
|
367
393
|
...newOutOfScopeChanges.map((file) => `- ${file.change}: ${file.path}`),
|
|
368
394
|
"",
|
|
369
|
-
].join("\n")
|
|
395
|
+
].join("\n"));
|
|
396
|
+
}
|
|
397
|
+
else if (changes.diff_redacted) {
|
|
398
|
+
state.finalStatus = "failed_policy_violation";
|
|
399
|
+
state.finalError = "Credential-like content was detected in the task diff. PatchWarden redacted the evidence; remove the sensitive content before retrying.";
|
|
370
400
|
}
|
|
371
401
|
else if (ctx.changePolicy === "no_changes" && changes.changed_files.length > 0) {
|
|
372
402
|
state.finalStatus = "failed_policy_violation";
|
|
@@ -375,7 +405,7 @@ function applyScopeViolationVerdict(ctx, state, changes, newOutOfScopeChanges, p
|
|
|
375
405
|
}
|
|
376
406
|
function finalizeTask(ctx, state, evidence) {
|
|
377
407
|
const { changes, artifactStatus, artifactCollectionError, artifactCollectionStartedAt, artifactCollectionFinishedAt, outOfScopeChanges, newOutOfScopeChanges, preexistingExternalDirty, preexistingWarnings, cleanupReport, artifactManifest, changedFileGroups } = evidence;
|
|
378
|
-
|
|
408
|
+
atomicWriteFileSync(join(ctx.taskDir, "test.log"), buildTestLog(state.testResult));
|
|
379
409
|
const verifyJson = buildVerifyJson(ctx.verifyCommands, state.verifyResults, ctx.repoPath);
|
|
380
410
|
if (newOutOfScopeChanges.length > 0) {
|
|
381
411
|
verifyJson.status = "failed";
|
|
@@ -383,11 +413,13 @@ function finalizeTask(ctx, state, evidence) {
|
|
|
383
413
|
}
|
|
384
414
|
else if (state.finalStatus === "failed_policy_violation") {
|
|
385
415
|
verifyJson.status = "failed";
|
|
386
|
-
verifyJson.failure_reason =
|
|
416
|
+
verifyJson.failure_reason = changes.diff_redacted
|
|
417
|
+
? "sensitive_content_violation"
|
|
418
|
+
: "change_policy_violation";
|
|
387
419
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
if (!["canceled", "done_by_agent", "failed_verification", "failed_scope_violation", "failed_policy_violation"].includes(state.finalStatus))
|
|
420
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "verify.json"), verifyJson);
|
|
421
|
+
atomicWriteFileSync(join(ctx.taskDir, "verify.log"), buildVerifyLog(verifyJson.commands));
|
|
422
|
+
if (!["canceled", "timeout", "done_by_agent", "failed_verification", "failed_scope_violation", "failed_policy_violation"].includes(state.finalStatus))
|
|
391
423
|
state.finalStatus = "failed";
|
|
392
424
|
const finalPhase = state.finalStatus === "done_by_agent" ? "done_by_agent" : state.finalStatus;
|
|
393
425
|
const followup = buildFailureFollowup(state.finalStatus, state.finalError, verifyJson.commands);
|
|
@@ -422,17 +454,17 @@ function finalizeTask(ctx, state, evidence) {
|
|
|
422
454
|
androidWarning,
|
|
423
455
|
preexistingWarnings,
|
|
424
456
|
});
|
|
425
|
-
|
|
457
|
+
atomicWriteFileSync(join(ctx.taskDir, "result.md"), resultMd);
|
|
426
458
|
const resultJson = buildResultJson({
|
|
427
459
|
ctx, state, evidence, verifyJson, followup, androidDiagnostic, androidWarning,
|
|
428
460
|
});
|
|
429
|
-
|
|
461
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "result.json"), resultJson);
|
|
430
462
|
if (state.finalError) {
|
|
431
463
|
const structuredError = errorPayload(state.lastCaughtError);
|
|
432
|
-
|
|
464
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "error.log"), {
|
|
433
465
|
summary: state.finalError,
|
|
434
466
|
...structuredError,
|
|
435
|
-
}
|
|
467
|
+
});
|
|
436
468
|
}
|
|
437
469
|
const finishedAt = new Date().toISOString();
|
|
438
470
|
updateStatus(ctx.taskDir, {
|
|
@@ -442,6 +474,10 @@ function finalizeTask(ctx, state, evidence) {
|
|
|
442
474
|
last_heartbeat_at: finishedAt,
|
|
443
475
|
finished_at: finishedAt,
|
|
444
476
|
error: state.finalError,
|
|
477
|
+
termination_reason: state.finalStatus === "timeout"
|
|
478
|
+
? "timeout"
|
|
479
|
+
: state.finalStatus === "canceled" ? "canceled" : null,
|
|
480
|
+
agent_failure_category: state.agentFailureCategory,
|
|
445
481
|
changed_files: changes.changed_files.map(({ path, change }) => ({ path, change })),
|
|
446
482
|
artifact_hygiene_counts: changes.artifact_hygiene.counts,
|
|
447
483
|
artifact_status: artifactStatus,
|
|
@@ -456,6 +492,8 @@ function finalizeTask(ctx, state, evidence) {
|
|
|
456
492
|
verify_commands: ctx.verifyCommands,
|
|
457
493
|
diff_available: changes.diff_available,
|
|
458
494
|
diff_truncated: changes.diff_truncated,
|
|
495
|
+
diff_redacted: changes.diff_redacted === true,
|
|
496
|
+
diff_redaction_categories: changes.diff_redaction_categories ?? [],
|
|
459
497
|
workspace_dirty_before: changes.workspace_dirty_before,
|
|
460
498
|
workspace_dirty_after: changes.workspace_dirty_after,
|
|
461
499
|
workspace_dirty: changes.workspace_dirty_after,
|
|
@@ -478,6 +516,7 @@ function buildResultJson(input) {
|
|
|
478
516
|
const { ctx, state, evidence, verifyJson, followup, androidDiagnostic, androidWarning } = input;
|
|
479
517
|
const { changes, artifactStatus, artifactCollectionError, artifactCollectionStartedAt, artifactCollectionFinishedAt, outOfScopeChanges, newOutOfScopeChanges, preexistingExternalDirty, cleanupReport, artifactManifest, changedFileGroups } = evidence;
|
|
480
518
|
return {
|
|
519
|
+
schema_version: ARTIFACT_SCHEMA_VERSION,
|
|
481
520
|
task_id: ctx.taskId,
|
|
482
521
|
status: state.finalStatus,
|
|
483
522
|
agent: ctx.agentName,
|
|
@@ -488,6 +527,10 @@ function buildResultJson(input) {
|
|
|
488
527
|
template: ctx.initialStatus.template || null,
|
|
489
528
|
change_policy: ctx.changePolicy,
|
|
490
529
|
summary: state.finalError || "Agent execution and configured verification completed successfully.",
|
|
530
|
+
termination_reason: state.finalStatus === "timeout"
|
|
531
|
+
? "timeout"
|
|
532
|
+
: state.finalStatus === "canceled" ? "canceled" : null,
|
|
533
|
+
agent_failure_category: state.agentFailureCategory,
|
|
491
534
|
changed_files: changes.changed_files,
|
|
492
535
|
changed_file_groups: {
|
|
493
536
|
source_changes: changedFileGroups.source_changes.length,
|
|
@@ -527,6 +570,9 @@ function buildResultJson(input) {
|
|
|
527
570
|
warnings: [
|
|
528
571
|
...ctx.beforeSnapshot.warnings,
|
|
529
572
|
...(changes.diff_truncated ? ["diff.patch was truncated; changed-files.json retains file evidence."] : []),
|
|
573
|
+
...(changes.diff_redacted ? [
|
|
574
|
+
`Credential-like diff content was redacted (${(changes.diff_redaction_categories ?? []).join(", ") || "sensitive_content"}).`,
|
|
575
|
+
] : []),
|
|
530
576
|
...evidence.preexistingWarnings,
|
|
531
577
|
...(androidWarning ? [androidWarning] : []),
|
|
532
578
|
],
|
|
@@ -594,13 +640,22 @@ async function runManagedProcess(options) {
|
|
|
594
640
|
if (Date.now() >= options.deadlineMs) {
|
|
595
641
|
return { exitCode: null, stdout: "", stderr: "", spawnError: null, terminationReason: "timeout" };
|
|
596
642
|
}
|
|
643
|
+
const exactRedactionValues = allowedEnvironmentValues(options.environmentVariableNames);
|
|
644
|
+
const logCapture = new SecureProcessLogCapture([options.stdoutPath, options.stderrPath], options.maxLogBytes);
|
|
597
645
|
let child;
|
|
598
646
|
try {
|
|
599
|
-
|
|
647
|
+
const env = buildChildEnvironment({
|
|
648
|
+
cwd: options.cwd,
|
|
649
|
+
allowedNames: options.environmentVariableNames,
|
|
650
|
+
blockedNames: options.blockedEnvironmentVariableNames,
|
|
651
|
+
});
|
|
652
|
+
const command = resolveTrustedExecutable(options.command, options.cwd, { pathValue: env.PATH });
|
|
653
|
+
child = spawn(command, options.args, {
|
|
600
654
|
cwd: options.cwd,
|
|
601
655
|
stdio: ["ignore", "pipe", "pipe"],
|
|
602
656
|
detached: process.platform !== "win32",
|
|
603
657
|
windowsHide: true,
|
|
658
|
+
env,
|
|
604
659
|
});
|
|
605
660
|
}
|
|
606
661
|
catch (error) {
|
|
@@ -611,9 +666,10 @@ async function runManagedProcess(options) {
|
|
|
611
666
|
writeTaskRuntime(options.taskDir, {
|
|
612
667
|
child_pid: child.pid,
|
|
613
668
|
child_started_at: new Date().toISOString(),
|
|
669
|
+
runner_pid: process.pid,
|
|
670
|
+
runner_instance_id: options.runnerInstanceId,
|
|
671
|
+
child_owned_by_runner_instance_id: options.runnerInstanceId,
|
|
614
672
|
});
|
|
615
|
-
const stdoutStream = openStream(options.stdoutPath);
|
|
616
|
-
const stderrStream = openStream(options.stderrPath);
|
|
617
673
|
let stdout = "";
|
|
618
674
|
let stderr = "";
|
|
619
675
|
let spawnError = null;
|
|
@@ -680,12 +736,12 @@ async function runManagedProcess(options) {
|
|
|
680
736
|
child.stdout?.on("data", (chunk) => {
|
|
681
737
|
const text = chunk.toString("utf-8");
|
|
682
738
|
stdout = appendBounded(stdout, text);
|
|
683
|
-
|
|
739
|
+
logCapture.append(options.stdoutPath, chunk);
|
|
684
740
|
});
|
|
685
741
|
child.stderr?.on("data", (chunk) => {
|
|
686
742
|
const text = chunk.toString("utf-8");
|
|
687
743
|
stderr = appendBounded(stderr, text);
|
|
688
|
-
|
|
744
|
+
logCapture.append(options.stderrPath, chunk);
|
|
689
745
|
});
|
|
690
746
|
heartbeat();
|
|
691
747
|
const heartbeatTimer = setInterval(heartbeat, HEARTBEAT_INTERVAL_MS);
|
|
@@ -702,12 +758,6 @@ async function runManagedProcess(options) {
|
|
|
702
758
|
};
|
|
703
759
|
childExitFinish = finish;
|
|
704
760
|
child.once("close", (code) => finish(code));
|
|
705
|
-
child.once("exit", (code) => {
|
|
706
|
-
// On Windows, "close" can lag behind "exit" when stdio pipes drain
|
|
707
|
-
// after taskkill. Resolve on exit to avoid hanging the runner, but
|
|
708
|
-
// still let "close" fire if it arrives first.
|
|
709
|
-
finish(code);
|
|
710
|
-
});
|
|
711
761
|
child.once("error", (error) => {
|
|
712
762
|
spawnError = error.message;
|
|
713
763
|
finish(null);
|
|
@@ -716,8 +766,7 @@ async function runManagedProcess(options) {
|
|
|
716
766
|
clearInterval(heartbeatTimer);
|
|
717
767
|
if (forceTimer)
|
|
718
768
|
clearTimeout(forceTimer);
|
|
719
|
-
|
|
720
|
-
stderrStream?.destroy();
|
|
769
|
+
logCapture.flush(exactRedactionValues);
|
|
721
770
|
writeTaskRuntime(options.taskDir, {
|
|
722
771
|
phase: options.phase,
|
|
723
772
|
last_heartbeat_at: new Date().toISOString(),
|
|
@@ -725,9 +774,17 @@ async function runManagedProcess(options) {
|
|
|
725
774
|
runner_pid: process.pid,
|
|
726
775
|
child_pid: undefined,
|
|
727
776
|
});
|
|
728
|
-
|
|
777
|
+
const safeStdout = redactProcessOutput(stdout, exactRedactionValues);
|
|
778
|
+
const safeStderr = redactProcessOutput(stderr, exactRedactionValues);
|
|
779
|
+
return {
|
|
780
|
+
exitCode,
|
|
781
|
+
stdout: safeStdout.length > MAX_CAPTURE_CHARS ? safeStdout.slice(-MAX_CAPTURE_CHARS) : safeStdout,
|
|
782
|
+
stderr: safeStderr.length > MAX_CAPTURE_CHARS ? safeStderr.slice(-MAX_CAPTURE_CHARS) : safeStderr,
|
|
783
|
+
spawnError,
|
|
784
|
+
terminationReason,
|
|
785
|
+
};
|
|
729
786
|
}
|
|
730
|
-
async function runTrustedTestCommand(testCommand, repoPath, taskDir, statusFile, deadlineMs) {
|
|
787
|
+
async function runTrustedTestCommand(testCommand, repoPath, taskDir, statusFile, deadlineMs, runnerInstanceId) {
|
|
731
788
|
const config = getConfig();
|
|
732
789
|
const trusted = guardTestCommand(testCommand, config, repoPath);
|
|
733
790
|
const startedAtMs = Date.now();
|
|
@@ -736,9 +793,9 @@ async function runTrustedTestCommand(testCommand, repoPath, taskDir, statusFile,
|
|
|
736
793
|
let command = parts[0];
|
|
737
794
|
let args = parts.slice(1);
|
|
738
795
|
if (process.platform === "win32" && /^(npm|npm\.cmd|pnpm|pnpm\.cmd)$/i.test(command)) {
|
|
739
|
-
const
|
|
740
|
-
command =
|
|
741
|
-
args = [
|
|
796
|
+
const packageManager = resolvePackageManagerInvocation(command, repoPath);
|
|
797
|
+
command = packageManager.command;
|
|
798
|
+
args = [...packageManager.argsPrefix, ...args];
|
|
742
799
|
}
|
|
743
800
|
const result = await runManagedProcess({
|
|
744
801
|
command,
|
|
@@ -749,6 +806,7 @@ async function runTrustedTestCommand(testCommand, repoPath, taskDir, statusFile,
|
|
|
749
806
|
phase: "running_tests",
|
|
750
807
|
currentCommand: trusted,
|
|
751
808
|
deadlineMs,
|
|
809
|
+
runnerInstanceId,
|
|
752
810
|
stdoutPath: join(taskDir, "test.stdout.log"),
|
|
753
811
|
stderrPath: join(taskDir, "test.stderr.log"),
|
|
754
812
|
});
|
|
@@ -963,10 +1021,10 @@ function normalizeTimeout(value, config) {
|
|
|
963
1021
|
function failBeforeExecution(taskId, taskDir, message, caughtError) {
|
|
964
1022
|
// Phase 9: Preserve PatchWardenError structured info in error.log
|
|
965
1023
|
const structuredError = caughtError ? errorPayload(caughtError) : { error: message };
|
|
966
|
-
|
|
1024
|
+
atomicWriteJsonFileSync(join(taskDir, "error.log"), {
|
|
967
1025
|
summary: message,
|
|
968
1026
|
...structuredError,
|
|
969
|
-
}
|
|
1027
|
+
});
|
|
970
1028
|
const current = readStatus(join(taskDir, "status.json"));
|
|
971
1029
|
const now = new Date().toISOString();
|
|
972
1030
|
const verify = {
|
|
@@ -974,17 +1032,17 @@ function failBeforeExecution(taskId, taskDir, message, caughtError) {
|
|
|
974
1032
|
requested_commands: Array.isArray(current.verify_commands) ? current.verify_commands : [],
|
|
975
1033
|
commands: [],
|
|
976
1034
|
};
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
1035
|
+
atomicWriteJsonFileSync(join(taskDir, "verify.json"), verify);
|
|
1036
|
+
atomicWriteFileSync(join(taskDir, "verify.log"), `Verification did not run: ${message}\n`);
|
|
1037
|
+
atomicWriteFileSync(join(taskDir, "test.log"), `(not run)\nExit code: not run\n${message}\n`);
|
|
1038
|
+
atomicWriteFileSync(join(taskDir, "git.diff"), "(task failed before change capture)\n");
|
|
1039
|
+
atomicWriteFileSync(join(taskDir, "diff.patch"), "(task failed before change capture)\n");
|
|
1040
|
+
atomicWriteJsonFileSync(join(taskDir, "file-stats.json"), {
|
|
983
1041
|
task_id: taskId,
|
|
984
1042
|
additions: 0,
|
|
985
1043
|
deletions: 0,
|
|
986
1044
|
files: [],
|
|
987
|
-
}
|
|
1045
|
+
});
|
|
988
1046
|
const result = {
|
|
989
1047
|
task_id: taskId,
|
|
990
1048
|
status: "failed",
|
|
@@ -1010,8 +1068,8 @@ function failBeforeExecution(taskId, taskDir, message, caughtError) {
|
|
|
1010
1068
|
safe_followup_prompt: "Inspect result.json and error.log, correct the task metadata or configuration, and retry without widening repository scope.",
|
|
1011
1069
|
next_steps: ["Fix task metadata or configuration and retry the task."],
|
|
1012
1070
|
};
|
|
1013
|
-
|
|
1014
|
-
|
|
1071
|
+
atomicWriteJsonFileSync(join(taskDir, "result.json"), result);
|
|
1072
|
+
atomicWriteFileSync(join(taskDir, "result.md"), `# PatchWarden Task Result\n\n## Status\nfailed\n\n## Summary\n${message}\n`);
|
|
1015
1073
|
updateStatus(taskDir, {
|
|
1016
1074
|
status: "failed",
|
|
1017
1075
|
phase: "failed",
|
|
@@ -1037,12 +1095,10 @@ function setTaskPhase(taskDir, phase, currentCommand, note) {
|
|
|
1037
1095
|
}
|
|
1038
1096
|
function updateStatus(taskDir, patch) {
|
|
1039
1097
|
const statusFile = join(taskDir, "status.json");
|
|
1040
|
-
|
|
1041
|
-
const next = { ...current, ...patch, updated_at: new Date().toISOString() };
|
|
1042
|
-
writeFileSync(statusFile, JSON.stringify(next, null, 2), "utf-8");
|
|
1098
|
+
updateTaskStatusFile(statusFile, patch);
|
|
1043
1099
|
}
|
|
1044
1100
|
function readStatus(statusFile) {
|
|
1045
|
-
return
|
|
1101
|
+
return readTaskStatusFile(statusFile);
|
|
1046
1102
|
}
|
|
1047
1103
|
function gracefulKill(child) {
|
|
1048
1104
|
try {
|
|
@@ -1058,10 +1114,13 @@ function forceKill(child) {
|
|
|
1058
1114
|
return;
|
|
1059
1115
|
try {
|
|
1060
1116
|
if (process.platform === "win32") {
|
|
1061
|
-
const
|
|
1117
|
+
const systemRoot = process.env.SystemRoot || process.env.WINDIR || "C:\\Windows";
|
|
1118
|
+
const taskkill = resolveTrustedExecutable(`${systemRoot}\\System32\\taskkill.exe`, process.cwd());
|
|
1119
|
+
const result = spawnSync(taskkill, ["/PID", String(child.pid), "/T", "/F"], {
|
|
1062
1120
|
stdio: "ignore",
|
|
1063
1121
|
timeout: 5000,
|
|
1064
1122
|
windowsHide: true,
|
|
1123
|
+
env: buildChildEnvironment({ cwd: process.cwd() }),
|
|
1065
1124
|
});
|
|
1066
1125
|
if (result.status !== 0)
|
|
1067
1126
|
child.kill("SIGKILL");
|
|
@@ -1077,9 +1136,6 @@ function forceKill(child) {
|
|
|
1077
1136
|
catch { } // cleanup failure is safe to ignore
|
|
1078
1137
|
}
|
|
1079
1138
|
}
|
|
1080
|
-
function openStream(path) {
|
|
1081
|
-
return path ? createWriteStream(path, { flags: "a" }) : null;
|
|
1082
|
-
}
|
|
1083
1139
|
function appendBounded(current, next) {
|
|
1084
1140
|
const combined = current + next;
|
|
1085
1141
|
return combined.length <= MAX_CAPTURE_CHARS
|
|
@@ -1089,3 +1145,16 @@ function appendBounded(current, next) {
|
|
|
1089
1145
|
function errorMessage(error) {
|
|
1090
1146
|
return error instanceof Error ? error.message : String(error);
|
|
1091
1147
|
}
|
|
1148
|
+
export function classifyAgentFailure(stderr) {
|
|
1149
|
+
const normalized = stderr.toLowerCase();
|
|
1150
|
+
if (normalized.includes("insufficient balance") || normalized.includes("insufficient credits")) {
|
|
1151
|
+
return "provider_insufficient_balance";
|
|
1152
|
+
}
|
|
1153
|
+
if (normalized.includes("unauthorized") || normalized.includes("authentication failed") || normalized.includes("invalid api key")) {
|
|
1154
|
+
return "provider_authentication_failed";
|
|
1155
|
+
}
|
|
1156
|
+
if (normalized.includes("model not found") || normalized.includes("model access") || normalized.includes("permission denied")) {
|
|
1157
|
+
return "provider_model_unavailable";
|
|
1158
|
+
}
|
|
1159
|
+
return null;
|
|
1160
|
+
}
|
|
@@ -7,6 +7,9 @@ export interface SimpleProcessOptions {
|
|
|
7
7
|
maxStderrBytes?: number;
|
|
8
8
|
stdoutPath?: string;
|
|
9
9
|
stderrPath?: string;
|
|
10
|
+
environmentVariableNames?: string[];
|
|
11
|
+
blockedEnvironmentVariableNames?: string[];
|
|
12
|
+
maxLogBytes?: number;
|
|
10
13
|
}
|
|
11
14
|
export interface SimpleProcessResult {
|
|
12
15
|
exitCode: number | null;
|