patchwarden 1.5.1 → 1.6.1
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 +152 -25
- package/README.md +120 -23
- package/dist/assessments/agentAssessor.js +21 -12
- package/dist/assessments/assessmentStore.js +42 -34
- 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 +46 -1
- package/dist/control/routes/status.js +72 -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 +63 -30
- package/dist/control/server.js +36 -365
- package/dist/control/shared.d.ts +25 -1
- package/dist/control/shared.js +64 -11
- 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 +127 -35
- package/dist/direct/directVerification.js +8 -16
- package/dist/doctor.js +62 -52
- 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 +1 -1
- package/dist/runner/runTask.js +115 -87
- 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} +1 -1
- package/dist/{taskRuntime.js → runner/taskRuntime.js} +3 -2
- package/dist/runner/taskStatusStore.d.ts +11 -0
- package/dist/runner/taskStatusStore.js +24 -0
- package/dist/runner/watch.d.ts +14 -1
- package/dist/runner/watch.js +256 -48
- 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 +24 -23
- 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} +1 -1
- package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +99 -29
- package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +6 -6
- package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +7 -7
- 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 +85 -0
- package/dist/tools/{createTask.d.ts → tasks/createTask.d.ts} +10 -2
- package/dist/tools/{createTask.js → tasks/createTask.js} +42 -27
- package/dist/tools/{diagnoseTask.d.ts → tasks/diagnoseTask.d.ts} +1 -1
- package/dist/tools/{diagnoseTask.js → tasks/diagnoseTask.js} +8 -8
- 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} +5 -5
- package/dist/tools/{getTaskStdoutTail.js → tasks/getTaskStdoutTail.js} +13 -11
- package/dist/tools/{getTaskSummary.js → tasks/getTaskSummary.js} +33 -26
- 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} +3 -3
- package/dist/tools/{reconcileTasks.d.ts → tasks/reconcileTasks.d.ts} +1 -1
- package/dist/tools/{reconcileTasks.js → tasks/reconcileTasks.js} +64 -66
- 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} +58 -30
- package/dist/tools/tasks/runVerification.js +38 -0
- package/dist/tools/{taskLineage.js → tasks/taskLineage.js} +8 -7
- package/dist/tools/{taskOutputs.js → tasks/taskOutputs.js} +17 -16
- 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/{listAgents.d.ts → workspace/listAgents.d.ts} +5 -0
- package/dist/tools/{listAgents.js → workspace/listAgents.js} +8 -4
- 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 +48 -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 +17 -11
- package/docs/release-checklist.md +1 -1
- package/docs/release-evidence.md +39 -29
- 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 +91 -6
- package/scripts/checks/control-smoke.js +19 -8
- package/scripts/checks/http-mcp-smoke.js +82 -4
- package/scripts/checks/lifecycle-smoke.js +10 -10
- package/scripts/checks/mcp-manifest-check.js +1 -1
- package/scripts/checks/mcp-smoke.js +27 -3
- package/scripts/checks/package-manifest-check.js +20 -7
- package/scripts/checks/unit-tests.js +3 -5
- package/scripts/checks/watcher-supervisor-smoke.js +9 -0
- 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 +126 -32
- 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/assessmentStore.ts +52 -40
- 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 +97 -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 +69 -33
- package/src/control/server.ts +36 -342
- package/src/control/shared.ts +77 -11
- 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 +211 -55
- package/src/direct/directVerification.ts +9 -23
- package/src/doctor.ts +73 -59
- 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 +138 -91
- package/src/runner/simpleProcess.ts +66 -31
- package/src/{taskProgress.ts → runner/taskProgress.ts} +3 -3
- package/src/{taskRuntime.ts → runner/taskRuntime.ts} +4 -3
- package/src/runner/taskStatusStore.ts +54 -0
- package/src/runner/watch.ts +281 -51
- 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 +25 -24
- 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} +99 -32
- package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +8 -8
- 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 +109 -0
- package/src/tools/{createTask.ts → tasks/createTask.ts} +49 -26
- package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +8 -8
- 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} +7 -5
- package/src/tools/{getTaskStdoutTail.ts → tasks/getTaskStdoutTail.ts} +13 -11
- package/src/tools/{getTaskSummary.ts → tasks/getTaskSummary.ts} +44 -33
- package/src/tools/tasks/index.ts +18 -0
- package/src/tools/{listTasks.ts → tasks/listTasks.ts} +4 -4
- package/src/tools/{reconcileTasks.ts → tasks/reconcileTasks.ts} +68 -66
- package/src/tools/tasks/retryTask.ts +69 -0
- package/src/tools/{runTaskLoop.ts → tasks/runTaskLoop.ts} +69 -34
- 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/{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/{listAgents.ts → workspace/listAgents.ts} +11 -4
- 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 +58 -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 +90 -147
- package/ui/pages/direct-sessions.html +123 -26
- package/ui/pages/getting-started.html +43 -0
- package/ui/pages/logs.html +105 -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 +245 -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/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/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/{taskOutputs.d.ts → tasks/taskOutputs.d.ts} +0 -0
- /package/dist/tools/{waitForTask.d.ts → tasks/waitForTask.d.ts} +0 -0
- /package/dist/tools/{waitForTask.js → tasks/waitForTask.js} +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/src/tools/{waitForTask.ts → tasks/waitForTask.ts} +0 -0
package/src/runner/runTask.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { spawn, spawnSync, type ChildProcess } from "node:child_process";
|
|
2
2
|
import {
|
|
3
|
-
createWriteStream,
|
|
4
3
|
existsSync,
|
|
5
4
|
readFileSync,
|
|
6
|
-
writeFileSync,
|
|
7
|
-
type WriteStream,
|
|
8
5
|
} from "node:fs";
|
|
9
6
|
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
10
7
|
import { getTasksDir, getPlansDir, getConfig, resolveWorkspaceRoot } from "../config.js";
|
|
@@ -12,11 +9,17 @@ import { guardPath, guardWorkspacePath } from "../security/pathGuard.js";
|
|
|
12
9
|
import {
|
|
13
10
|
guardTestCommand,
|
|
14
11
|
} from "../security/commandGuard.js";
|
|
15
|
-
import { writeTaskProgress } from "
|
|
16
|
-
import { writeTaskRuntime } from "
|
|
12
|
+
import { writeTaskProgress } from "./taskProgress.js";
|
|
13
|
+
import { writeTaskRuntime } from "./taskRuntime.js";
|
|
14
|
+
import {
|
|
15
|
+
claimPendingTask,
|
|
16
|
+
readTaskStatusFile,
|
|
17
|
+
updateTaskStatusFile,
|
|
18
|
+
type TaskStatusRecord,
|
|
19
|
+
} from "./taskStatusStore.js";
|
|
17
20
|
import { validateAssessmentFreshness } from "../assessments/assessmentStore.js";
|
|
18
21
|
import { buildAgentInvocation, buildExecutionPrompt } from "./agentInvocation.js";
|
|
19
|
-
import type { TaskPhase, TaskStatus } from "../tools/createTask.js";
|
|
22
|
+
import type { TaskPhase, TaskStatus } from "../tools/tasks/createTask.js";
|
|
20
23
|
import {
|
|
21
24
|
buildChangeArtifacts,
|
|
22
25
|
captureRepoSnapshot,
|
|
@@ -35,8 +38,18 @@ import {
|
|
|
35
38
|
type ChangedFileGroups,
|
|
36
39
|
} from "./changeCapture.js";
|
|
37
40
|
import { PatchWardenError, errorPayload } from "../errors.js";
|
|
38
|
-
import {
|
|
41
|
+
import { ARTIFACT_SCHEMA_VERSION } from "../version.js";
|
|
42
|
+
import { diagnoseAndroidBuild } from "../tools/workspace/androidDoctor.js";
|
|
39
43
|
import { runPostTaskCleanup, type PostTaskCleanupReport } from "./postTaskCleanup.js";
|
|
44
|
+
import { atomicWriteFileSync, atomicWriteJsonFileSync } from "../utils/atomicFile.js";
|
|
45
|
+
import {
|
|
46
|
+
allowedEnvironmentValues,
|
|
47
|
+
buildChildEnvironment,
|
|
48
|
+
redactProcessOutput,
|
|
49
|
+
resolvePackageManagerInvocation,
|
|
50
|
+
resolveTrustedExecutable,
|
|
51
|
+
SecureProcessLogCapture,
|
|
52
|
+
} from "./processSecurity.js";
|
|
40
53
|
|
|
41
54
|
const HEARTBEAT_INTERVAL_MS = 2000;
|
|
42
55
|
const GRACEFUL_KILL_MS = 2000;
|
|
@@ -95,7 +108,7 @@ interface TaskContext {
|
|
|
95
108
|
wsRoot: string;
|
|
96
109
|
config: ReturnType<typeof getConfig>;
|
|
97
110
|
plansDir: string;
|
|
98
|
-
initialStatus:
|
|
111
|
+
initialStatus: TaskStatusRecord;
|
|
99
112
|
planId: string;
|
|
100
113
|
agentName: string;
|
|
101
114
|
testCommand: string;
|
|
@@ -154,7 +167,25 @@ async function prepareTask(taskId: string): Promise<TaskContext | TaskRunResult>
|
|
|
154
167
|
const statusFile = join(taskDir, "status.json");
|
|
155
168
|
if (!existsSync(statusFile)) throw new Error(`Task not found: "${taskId}"`);
|
|
156
169
|
|
|
157
|
-
const
|
|
170
|
+
const startedAtMs = Date.now();
|
|
171
|
+
const claim = claimPendingTask(statusFile, {
|
|
172
|
+
status: "running",
|
|
173
|
+
phase: "preparing",
|
|
174
|
+
started_at: new Date(startedAtMs).toISOString(),
|
|
175
|
+
last_heartbeat_at: new Date(startedAtMs).toISOString(),
|
|
176
|
+
current_command: null,
|
|
177
|
+
error: null,
|
|
178
|
+
});
|
|
179
|
+
if (!claim.claimed) {
|
|
180
|
+
const currentStatus = String(claim.status.status || "failed") as TaskStatus;
|
|
181
|
+
return {
|
|
182
|
+
task_id: taskId,
|
|
183
|
+
status: currentStatus,
|
|
184
|
+
error: `Task is ${currentStatus}; only pending tasks can be claimed for execution.`,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const initialStatus = claim.status;
|
|
158
189
|
const planId = String(initialStatus.plan_id || "");
|
|
159
190
|
const agentName = String(initialStatus.agent || "");
|
|
160
191
|
const rawRepoPath = String(initialStatus.resolved_repo_path || initialStatus.repo_path || wsRoot);
|
|
@@ -167,7 +198,6 @@ async function prepareTask(taskId: string): Promise<TaskContext | TaskRunResult>
|
|
|
167
198
|
} catch (error) {
|
|
168
199
|
return failBeforeExecution(taskId, taskDir, errorMessage(error), error);
|
|
169
200
|
}
|
|
170
|
-
const startedAtMs = Date.now();
|
|
171
201
|
const deadlineMs = startedAtMs + timeoutSeconds * 1000;
|
|
172
202
|
|
|
173
203
|
let repoPath: string;
|
|
@@ -199,15 +229,7 @@ async function prepareTask(taskId: string): Promise<TaskContext | TaskRunResult>
|
|
|
199
229
|
}
|
|
200
230
|
}
|
|
201
231
|
|
|
202
|
-
updateStatus(taskDir, {
|
|
203
|
-
status: "running",
|
|
204
|
-
phase: "preparing",
|
|
205
|
-
started_at: new Date(startedAtMs).toISOString(),
|
|
206
|
-
last_heartbeat_at: new Date().toISOString(),
|
|
207
|
-
timeout_seconds: timeoutSeconds,
|
|
208
|
-
current_command: null,
|
|
209
|
-
error: null,
|
|
210
|
-
});
|
|
232
|
+
updateStatus(taskDir, { timeout_seconds: timeoutSeconds });
|
|
211
233
|
writeTaskRuntime(taskDir, {
|
|
212
234
|
task_started_at: new Date(startedAtMs).toISOString(),
|
|
213
235
|
watcher_instance_id: process.env.PATCHWARDEN_WATCHER_INSTANCE_ID || undefined,
|
|
@@ -264,6 +286,8 @@ async function executeAgent(ctx: TaskContext): Promise<ExecutionState> {
|
|
|
264
286
|
deadlineMs: ctx.deadlineMs,
|
|
265
287
|
stdoutPath: join(ctx.taskDir, "stdout.log"),
|
|
266
288
|
stderrPath: join(ctx.taskDir, "stderr.log"),
|
|
289
|
+
environmentVariableNames: invocation.environmentVariableNames,
|
|
290
|
+
blockedEnvironmentVariableNames: invocation.blockedEnvironmentVariableNames,
|
|
267
291
|
});
|
|
268
292
|
|
|
269
293
|
const agentResult = state.agentResult;
|
|
@@ -342,7 +366,7 @@ async function collectArtifacts(ctx: TaskContext, state: ExecutionState): Promis
|
|
|
342
366
|
skipped: [{ path: ".", reason: "post_task_cleanup", skip_reason: errorMessage(error) }],
|
|
343
367
|
source_files_touched: 0,
|
|
344
368
|
};
|
|
345
|
-
|
|
369
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "post-task-cleanup.json"), cleanupReport);
|
|
346
370
|
updateStatus(ctx.taskDir, { cleanup: cleanupReport });
|
|
347
371
|
}
|
|
348
372
|
}
|
|
@@ -353,21 +377,20 @@ async function collectArtifacts(ctx: TaskContext, state: ExecutionState): Promis
|
|
|
353
377
|
let artifactStatus: "collected" | "partial" | "failed" | "timeout" = "collected";
|
|
354
378
|
let artifactCollectionError: string | null = null;
|
|
355
379
|
let timeoutHandle: ReturnType<typeof setTimeout> | undefined;
|
|
380
|
+
const artifactController = new AbortController();
|
|
356
381
|
try {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
changes = collectionResult;
|
|
382
|
+
timeoutHandle = setTimeout(() => {
|
|
383
|
+
artifactController.abort(new Error("Artifact collection timed out"));
|
|
384
|
+
}, ARTIFACT_COLLECTION_TIMEOUT_MS);
|
|
385
|
+
const afterSnapshot = await captureRepoSnapshot(ctx.repoPath, artifactController.signal);
|
|
386
|
+
writeSnapshot(ctx.taskDir, "git-after.json", afterSnapshot);
|
|
387
|
+
changes = await buildChangeArtifacts(
|
|
388
|
+
ctx.repoPath,
|
|
389
|
+
ctx.beforeSnapshot,
|
|
390
|
+
afterSnapshot,
|
|
391
|
+
artifactController.signal,
|
|
392
|
+
);
|
|
369
393
|
} catch (error) {
|
|
370
|
-
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
371
394
|
state.lastCaughtError = error;
|
|
372
395
|
artifactCollectionError = errorMessage(error);
|
|
373
396
|
artifactStatus = artifactCollectionError.includes("timed out") ? "timeout" : "failed";
|
|
@@ -386,7 +409,7 @@ async function collectArtifacts(ctx: TaskContext, state: ExecutionState): Promis
|
|
|
386
409
|
unavailable_reason: `Change capture failed: ${artifactCollectionError}`,
|
|
387
410
|
artifact_hygiene: emptyArtifactHygiene(),
|
|
388
411
|
};
|
|
389
|
-
|
|
412
|
+
atomicWriteFileSync(join(ctx.taskDir, "partial_result.md"), [
|
|
390
413
|
"# PatchWarden Partial Result",
|
|
391
414
|
"",
|
|
392
415
|
`Task: ${ctx.taskId}`,
|
|
@@ -406,13 +429,15 @@ async function collectArtifacts(ctx: TaskContext, state: ExecutionState): Promis
|
|
|
406
429
|
"## Note",
|
|
407
430
|
"Artifact collection did not complete. The task result may be incomplete. Review stdout.log, stderr.log, and verify.json for details.",
|
|
408
431
|
"",
|
|
409
|
-
].join("\n")
|
|
432
|
+
].join("\n"));
|
|
410
433
|
if (state.finalStatus === "done_by_agent") {
|
|
411
434
|
state.finalError = `Change capture failed: ${artifactCollectionError}`;
|
|
412
435
|
state.finalStatus = "failed";
|
|
413
436
|
} else {
|
|
414
437
|
state.finalError ||= `Change capture failed: ${artifactCollectionError}`;
|
|
415
438
|
}
|
|
439
|
+
} finally {
|
|
440
|
+
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
416
441
|
}
|
|
417
442
|
const artifactCollectionFinishedAt = new Date().toISOString();
|
|
418
443
|
|
|
@@ -440,18 +465,18 @@ async function collectArtifacts(ctx: TaskContext, state: ExecutionState): Promis
|
|
|
440
465
|
|
|
441
466
|
applyScopeViolationVerdict(ctx, state, changes, newOutOfScopeChanges, preexistingExternalDirty);
|
|
442
467
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
468
|
+
atomicWriteFileSync(join(ctx.taskDir, "git.diff"), changes.diff);
|
|
469
|
+
atomicWriteFileSync(join(ctx.taskDir, "diff.patch"), changes.diff);
|
|
470
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "changed-files.json"), { schema_version: ARTIFACT_SCHEMA_VERSION, ...changes });
|
|
446
471
|
const artifactManifest = await buildArtifactManifest(changes.changed_files, ctx.repoPath, ctx.taskId);
|
|
447
|
-
|
|
472
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "artifact_manifest.json"), artifactManifest);
|
|
448
473
|
const changedFileGroups = groupChangedFiles(changes.changed_files);
|
|
449
|
-
|
|
474
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "file-stats.json"), {
|
|
450
475
|
task_id: ctx.taskId,
|
|
451
476
|
additions: changes.additions,
|
|
452
477
|
deletions: changes.deletions,
|
|
453
478
|
files: changes.file_stats,
|
|
454
|
-
}
|
|
479
|
+
});
|
|
455
480
|
|
|
456
481
|
return {
|
|
457
482
|
changes, artifactStatus, artifactCollectionError, artifactCollectionStartedAt, artifactCollectionFinishedAt,
|
|
@@ -470,15 +495,15 @@ function applyScopeViolationVerdict(
|
|
|
470
495
|
if (newOutOfScopeChanges.length > 0) {
|
|
471
496
|
state.finalStatus = "failed_scope_violation";
|
|
472
497
|
state.finalError = `Detected ${newOutOfScopeChanges.length} new change(s) outside resolved_repo_path during task execution.`;
|
|
473
|
-
|
|
498
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "rollback-plan.json"), {
|
|
474
499
|
task_id: ctx.taskId,
|
|
475
500
|
status: "review_required",
|
|
476
501
|
automatic_rollback_performed: false,
|
|
477
502
|
warning: "Review ownership and concurrent edits before rollback. PatchWarden did not modify or restore these files.",
|
|
478
503
|
out_of_scope_changes: newOutOfScopeChanges,
|
|
479
504
|
preexisting_external_dirty_files: preexistingExternalDirty,
|
|
480
|
-
}
|
|
481
|
-
|
|
505
|
+
});
|
|
506
|
+
atomicWriteFileSync(join(ctx.taskDir, "rollback_scope_violation_plan.md"), [
|
|
482
507
|
"# Scope Violation Rollback Plan",
|
|
483
508
|
"",
|
|
484
509
|
`Task: ${ctx.taskId}`,
|
|
@@ -488,7 +513,10 @@ function applyScopeViolationVerdict(
|
|
|
488
513
|
"## New out-of-scope files (caused by this task)",
|
|
489
514
|
...newOutOfScopeChanges.map((file) => `- ${file.change}: ${file.path}`),
|
|
490
515
|
"",
|
|
491
|
-
].join("\n")
|
|
516
|
+
].join("\n"));
|
|
517
|
+
} else if (changes.diff_redacted) {
|
|
518
|
+
state.finalStatus = "failed_policy_violation";
|
|
519
|
+
state.finalError = "Credential-like content was detected in the task diff. PatchWarden redacted the evidence; remove the sensitive content before retrying.";
|
|
492
520
|
} else if (ctx.changePolicy === "no_changes" && changes.changed_files.length > 0) {
|
|
493
521
|
state.finalStatus = "failed_policy_violation";
|
|
494
522
|
state.finalError = `Task policy requires no repository changes, but detected ${changes.changed_files.length} change(s).`;
|
|
@@ -500,17 +528,19 @@ function finalizeTask(ctx: TaskContext, state: ExecutionState, evidence: Artifac
|
|
|
500
528
|
outOfScopeChanges, newOutOfScopeChanges, preexistingExternalDirty, preexistingWarnings, cleanupReport,
|
|
501
529
|
artifactManifest, changedFileGroups } = evidence;
|
|
502
530
|
|
|
503
|
-
|
|
531
|
+
atomicWriteFileSync(join(ctx.taskDir, "test.log"), buildTestLog(state.testResult));
|
|
504
532
|
const verifyJson = buildVerifyJson(ctx.verifyCommands, state.verifyResults, ctx.repoPath);
|
|
505
533
|
if (newOutOfScopeChanges.length > 0) {
|
|
506
534
|
verifyJson.status = "failed";
|
|
507
535
|
verifyJson.failure_reason = "scope_violation";
|
|
508
536
|
} else if (state.finalStatus === "failed_policy_violation") {
|
|
509
537
|
verifyJson.status = "failed";
|
|
510
|
-
verifyJson.failure_reason =
|
|
538
|
+
verifyJson.failure_reason = changes.diff_redacted
|
|
539
|
+
? "sensitive_content_violation"
|
|
540
|
+
: "change_policy_violation";
|
|
511
541
|
}
|
|
512
|
-
|
|
513
|
-
|
|
542
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "verify.json"), verifyJson);
|
|
543
|
+
atomicWriteFileSync(join(ctx.taskDir, "verify.log"), buildVerifyLog(verifyJson.commands));
|
|
514
544
|
|
|
515
545
|
if (!["canceled", "done_by_agent", "failed_verification", "failed_scope_violation", "failed_policy_violation"].includes(state.finalStatus)) state.finalStatus = "failed";
|
|
516
546
|
const finalPhase: TaskPhase = state.finalStatus === "done_by_agent" ? "done_by_agent" : (state.finalStatus as TaskPhase);
|
|
@@ -547,19 +577,19 @@ function finalizeTask(ctx: TaskContext, state: ExecutionState, evidence: Artifac
|
|
|
547
577
|
androidWarning,
|
|
548
578
|
preexistingWarnings,
|
|
549
579
|
});
|
|
550
|
-
|
|
580
|
+
atomicWriteFileSync(join(ctx.taskDir, "result.md"), resultMd);
|
|
551
581
|
|
|
552
582
|
const resultJson = buildResultJson({
|
|
553
583
|
ctx, state, evidence, verifyJson, followup, androidDiagnostic, androidWarning,
|
|
554
584
|
});
|
|
555
|
-
|
|
585
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "result.json"), resultJson);
|
|
556
586
|
|
|
557
587
|
if (state.finalError) {
|
|
558
588
|
const structuredError = errorPayload(state.lastCaughtError);
|
|
559
|
-
|
|
589
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "error.log"), {
|
|
560
590
|
summary: state.finalError,
|
|
561
591
|
...structuredError,
|
|
562
|
-
}
|
|
592
|
+
});
|
|
563
593
|
}
|
|
564
594
|
|
|
565
595
|
const finishedAt = new Date().toISOString();
|
|
@@ -584,6 +614,8 @@ function finalizeTask(ctx: TaskContext, state: ExecutionState, evidence: Artifac
|
|
|
584
614
|
verify_commands: ctx.verifyCommands,
|
|
585
615
|
diff_available: changes.diff_available,
|
|
586
616
|
diff_truncated: changes.diff_truncated,
|
|
617
|
+
diff_redacted: changes.diff_redacted === true,
|
|
618
|
+
diff_redaction_categories: changes.diff_redaction_categories ?? [],
|
|
587
619
|
workspace_dirty_before: changes.workspace_dirty_before,
|
|
588
620
|
workspace_dirty_after: changes.workspace_dirty_after,
|
|
589
621
|
workspace_dirty: changes.workspace_dirty_after,
|
|
@@ -619,6 +651,7 @@ function buildResultJson(input: {
|
|
|
619
651
|
preexistingExternalDirty, cleanupReport, artifactManifest, changedFileGroups } = evidence;
|
|
620
652
|
|
|
621
653
|
return {
|
|
654
|
+
schema_version: ARTIFACT_SCHEMA_VERSION,
|
|
622
655
|
task_id: ctx.taskId,
|
|
623
656
|
status: state.finalStatus,
|
|
624
657
|
agent: ctx.agentName,
|
|
@@ -668,6 +701,9 @@ function buildResultJson(input: {
|
|
|
668
701
|
warnings: [
|
|
669
702
|
...ctx.beforeSnapshot.warnings,
|
|
670
703
|
...(changes.diff_truncated ? ["diff.patch was truncated; changed-files.json retains file evidence."] : []),
|
|
704
|
+
...(changes.diff_redacted ? [
|
|
705
|
+
`Credential-like diff content was redacted (${(changes.diff_redaction_categories ?? []).join(", ") || "sensitive_content"}).`,
|
|
706
|
+
] : []),
|
|
671
707
|
...evidence.preexistingWarnings,
|
|
672
708
|
...(androidWarning ? [androidWarning] : []),
|
|
673
709
|
],
|
|
@@ -753,18 +789,33 @@ async function runManagedProcess(options: {
|
|
|
753
789
|
deadlineMs: number;
|
|
754
790
|
stdoutPath?: string;
|
|
755
791
|
stderrPath?: string;
|
|
792
|
+
environmentVariableNames?: string[];
|
|
793
|
+
blockedEnvironmentVariableNames?: string[];
|
|
794
|
+
maxLogBytes?: number;
|
|
756
795
|
}): Promise<ManagedProcessResult> {
|
|
757
796
|
if (Date.now() >= options.deadlineMs) {
|
|
758
797
|
return { exitCode: null, stdout: "", stderr: "", spawnError: null, terminationReason: "timeout" };
|
|
759
798
|
}
|
|
760
799
|
|
|
800
|
+
const exactRedactionValues = allowedEnvironmentValues(options.environmentVariableNames);
|
|
801
|
+
const logCapture = new SecureProcessLogCapture(
|
|
802
|
+
[options.stdoutPath, options.stderrPath],
|
|
803
|
+
options.maxLogBytes,
|
|
804
|
+
);
|
|
761
805
|
let child: ChildProcess;
|
|
762
806
|
try {
|
|
763
|
-
|
|
807
|
+
const env = buildChildEnvironment({
|
|
808
|
+
cwd: options.cwd,
|
|
809
|
+
allowedNames: options.environmentVariableNames,
|
|
810
|
+
blockedNames: options.blockedEnvironmentVariableNames,
|
|
811
|
+
});
|
|
812
|
+
const command = resolveTrustedExecutable(options.command, options.cwd, { pathValue: env.PATH });
|
|
813
|
+
child = spawn(command, options.args, {
|
|
764
814
|
cwd: options.cwd,
|
|
765
815
|
stdio: ["ignore", "pipe", "pipe"],
|
|
766
816
|
detached: process.platform !== "win32",
|
|
767
817
|
windowsHide: true,
|
|
818
|
+
env,
|
|
768
819
|
});
|
|
769
820
|
} catch (error) {
|
|
770
821
|
return { exitCode: null, stdout: "", stderr: "", spawnError: errorMessage(error), terminationReason: null };
|
|
@@ -776,8 +827,6 @@ async function runManagedProcess(options: {
|
|
|
776
827
|
child_started_at: new Date().toISOString(),
|
|
777
828
|
});
|
|
778
829
|
|
|
779
|
-
const stdoutStream = openStream(options.stdoutPath);
|
|
780
|
-
const stderrStream = openStream(options.stderrPath);
|
|
781
830
|
let stdout = "";
|
|
782
831
|
let stderr = "";
|
|
783
832
|
let spawnError: string | null = null;
|
|
@@ -836,12 +885,12 @@ async function runManagedProcess(options: {
|
|
|
836
885
|
child.stdout?.on("data", (chunk: Buffer) => {
|
|
837
886
|
const text = chunk.toString("utf-8");
|
|
838
887
|
stdout = appendBounded(stdout, text);
|
|
839
|
-
|
|
888
|
+
logCapture.append(options.stdoutPath, chunk);
|
|
840
889
|
});
|
|
841
890
|
child.stderr?.on("data", (chunk: Buffer) => {
|
|
842
891
|
const text = chunk.toString("utf-8");
|
|
843
892
|
stderr = appendBounded(stderr, text);
|
|
844
|
-
|
|
893
|
+
logCapture.append(options.stderrPath, chunk);
|
|
845
894
|
});
|
|
846
895
|
|
|
847
896
|
heartbeat();
|
|
@@ -857,12 +906,6 @@ async function runManagedProcess(options: {
|
|
|
857
906
|
};
|
|
858
907
|
childExitFinish = finish;
|
|
859
908
|
child.once("close", (code) => finish(code));
|
|
860
|
-
child.once("exit", (code) => {
|
|
861
|
-
// On Windows, "close" can lag behind "exit" when stdio pipes drain
|
|
862
|
-
// after taskkill. Resolve on exit to avoid hanging the runner, but
|
|
863
|
-
// still let "close" fire if it arrives first.
|
|
864
|
-
finish(code);
|
|
865
|
-
});
|
|
866
909
|
child.once("error", (error) => {
|
|
867
910
|
spawnError = error.message;
|
|
868
911
|
finish(null);
|
|
@@ -871,8 +914,7 @@ async function runManagedProcess(options: {
|
|
|
871
914
|
|
|
872
915
|
clearInterval(heartbeatTimer);
|
|
873
916
|
if (forceTimer) clearTimeout(forceTimer);
|
|
874
|
-
|
|
875
|
-
stderrStream?.destroy();
|
|
917
|
+
logCapture.flush(exactRedactionValues);
|
|
876
918
|
writeTaskRuntime(options.taskDir, {
|
|
877
919
|
phase: options.phase,
|
|
878
920
|
last_heartbeat_at: new Date().toISOString(),
|
|
@@ -880,8 +922,16 @@ async function runManagedProcess(options: {
|
|
|
880
922
|
runner_pid: process.pid,
|
|
881
923
|
child_pid: undefined,
|
|
882
924
|
});
|
|
925
|
+
const safeStdout = redactProcessOutput(stdout, exactRedactionValues);
|
|
926
|
+
const safeStderr = redactProcessOutput(stderr, exactRedactionValues);
|
|
883
927
|
|
|
884
|
-
return {
|
|
928
|
+
return {
|
|
929
|
+
exitCode,
|
|
930
|
+
stdout: safeStdout.length > MAX_CAPTURE_CHARS ? safeStdout.slice(-MAX_CAPTURE_CHARS) : safeStdout,
|
|
931
|
+
stderr: safeStderr.length > MAX_CAPTURE_CHARS ? safeStderr.slice(-MAX_CAPTURE_CHARS) : safeStderr,
|
|
932
|
+
spawnError,
|
|
933
|
+
terminationReason,
|
|
934
|
+
};
|
|
885
935
|
}
|
|
886
936
|
|
|
887
937
|
async function runTrustedTestCommand(
|
|
@@ -899,9 +949,9 @@ async function runTrustedTestCommand(
|
|
|
899
949
|
let command = parts[0];
|
|
900
950
|
let args = parts.slice(1);
|
|
901
951
|
if (process.platform === "win32" && /^(npm|npm\.cmd|pnpm|pnpm\.cmd)$/i.test(command)) {
|
|
902
|
-
const
|
|
903
|
-
command =
|
|
904
|
-
args = [
|
|
952
|
+
const packageManager = resolvePackageManagerInvocation(command, repoPath);
|
|
953
|
+
command = packageManager.command;
|
|
954
|
+
args = [...packageManager.argsPrefix, ...args];
|
|
905
955
|
}
|
|
906
956
|
const result = await runManagedProcess({
|
|
907
957
|
command,
|
|
@@ -1161,10 +1211,10 @@ function normalizeTimeout(value: unknown, config: ReturnType<typeof getConfig>):
|
|
|
1161
1211
|
function failBeforeExecution(taskId: string, taskDir: string, message: string, caughtError?: unknown): TaskRunResult {
|
|
1162
1212
|
// Phase 9: Preserve PatchWardenError structured info in error.log
|
|
1163
1213
|
const structuredError = caughtError ? errorPayload(caughtError) : { error: message };
|
|
1164
|
-
|
|
1214
|
+
atomicWriteJsonFileSync(join(taskDir, "error.log"), {
|
|
1165
1215
|
summary: message,
|
|
1166
1216
|
...structuredError,
|
|
1167
|
-
}
|
|
1217
|
+
});
|
|
1168
1218
|
const current = readStatus(join(taskDir, "status.json"));
|
|
1169
1219
|
const now = new Date().toISOString();
|
|
1170
1220
|
const verify: VerifyReport = {
|
|
@@ -1172,17 +1222,17 @@ function failBeforeExecution(taskId: string, taskDir: string, message: string, c
|
|
|
1172
1222
|
requested_commands: Array.isArray(current.verify_commands) ? current.verify_commands : [],
|
|
1173
1223
|
commands: [],
|
|
1174
1224
|
};
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1225
|
+
atomicWriteJsonFileSync(join(taskDir, "verify.json"), verify);
|
|
1226
|
+
atomicWriteFileSync(join(taskDir, "verify.log"), `Verification did not run: ${message}\n`);
|
|
1227
|
+
atomicWriteFileSync(join(taskDir, "test.log"), `(not run)\nExit code: not run\n${message}\n`);
|
|
1228
|
+
atomicWriteFileSync(join(taskDir, "git.diff"), "(task failed before change capture)\n");
|
|
1229
|
+
atomicWriteFileSync(join(taskDir, "diff.patch"), "(task failed before change capture)\n");
|
|
1230
|
+
atomicWriteJsonFileSync(join(taskDir, "file-stats.json"), {
|
|
1181
1231
|
task_id: taskId,
|
|
1182
1232
|
additions: 0,
|
|
1183
1233
|
deletions: 0,
|
|
1184
1234
|
files: [],
|
|
1185
|
-
}
|
|
1235
|
+
});
|
|
1186
1236
|
const result = {
|
|
1187
1237
|
task_id: taskId,
|
|
1188
1238
|
status: "failed",
|
|
@@ -1208,8 +1258,8 @@ function failBeforeExecution(taskId: string, taskDir: string, message: string, c
|
|
|
1208
1258
|
safe_followup_prompt: "Inspect result.json and error.log, correct the task metadata or configuration, and retry without widening repository scope.",
|
|
1209
1259
|
next_steps: ["Fix task metadata or configuration and retry the task."],
|
|
1210
1260
|
};
|
|
1211
|
-
|
|
1212
|
-
|
|
1261
|
+
atomicWriteJsonFileSync(join(taskDir, "result.json"), result);
|
|
1262
|
+
atomicWriteFileSync(join(taskDir, "result.md"), `# PatchWarden Task Result\n\n## Status\nfailed\n\n## Summary\n${message}\n`);
|
|
1213
1263
|
updateStatus(taskDir, {
|
|
1214
1264
|
status: "failed",
|
|
1215
1265
|
phase: "failed",
|
|
@@ -1242,13 +1292,11 @@ function setTaskPhase(
|
|
|
1242
1292
|
|
|
1243
1293
|
function updateStatus(taskDir: string, patch: Record<string, unknown>): void {
|
|
1244
1294
|
const statusFile = join(taskDir, "status.json");
|
|
1245
|
-
|
|
1246
|
-
const next = { ...current, ...patch, updated_at: new Date().toISOString() };
|
|
1247
|
-
writeFileSync(statusFile, JSON.stringify(next, null, 2), "utf-8");
|
|
1295
|
+
updateTaskStatusFile(statusFile, patch);
|
|
1248
1296
|
}
|
|
1249
1297
|
|
|
1250
|
-
function readStatus(statusFile: string):
|
|
1251
|
-
return
|
|
1298
|
+
function readStatus(statusFile: string): TaskStatusRecord {
|
|
1299
|
+
return readTaskStatusFile(statusFile);
|
|
1252
1300
|
}
|
|
1253
1301
|
|
|
1254
1302
|
function gracefulKill(child: ChildProcess): void {
|
|
@@ -1262,10 +1310,13 @@ function forceKill(child: ChildProcess): void {
|
|
|
1262
1310
|
if (!child.pid) return;
|
|
1263
1311
|
try {
|
|
1264
1312
|
if (process.platform === "win32") {
|
|
1265
|
-
const
|
|
1313
|
+
const systemRoot = process.env.SystemRoot || process.env.WINDIR || "C:\\Windows";
|
|
1314
|
+
const taskkill = resolveTrustedExecutable(`${systemRoot}\\System32\\taskkill.exe`, process.cwd());
|
|
1315
|
+
const result = spawnSync(taskkill, ["/PID", String(child.pid), "/T", "/F"], {
|
|
1266
1316
|
stdio: "ignore",
|
|
1267
1317
|
timeout: 5000,
|
|
1268
1318
|
windowsHide: true,
|
|
1319
|
+
env: buildChildEnvironment({ cwd: process.cwd() }),
|
|
1269
1320
|
});
|
|
1270
1321
|
if (result.status !== 0) child.kill("SIGKILL");
|
|
1271
1322
|
} else {
|
|
@@ -1276,10 +1327,6 @@ function forceKill(child: ChildProcess): void {
|
|
|
1276
1327
|
}
|
|
1277
1328
|
}
|
|
1278
1329
|
|
|
1279
|
-
function openStream(path?: string): WriteStream | null {
|
|
1280
|
-
return path ? createWriteStream(path, { flags: "a" }) : null;
|
|
1281
|
-
}
|
|
1282
|
-
|
|
1283
1330
|
function appendBounded(current: string, next: string): string {
|
|
1284
1331
|
const combined = current + next;
|
|
1285
1332
|
return combined.length <= MAX_CAPTURE_CHARS
|