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/dist/runner/runTask.js
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
-
import {
|
|
2
|
+
import { existsSync, readFileSync, } from "node:fs";
|
|
3
3
|
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
4
4
|
import { getTasksDir, getPlansDir, getConfig, resolveWorkspaceRoot } from "../config.js";
|
|
5
5
|
import { guardPath, guardWorkspacePath } from "../security/pathGuard.js";
|
|
6
6
|
import { guardTestCommand, } from "../security/commandGuard.js";
|
|
7
|
-
import { writeTaskProgress } from "
|
|
8
|
-
import { writeTaskRuntime } from "
|
|
7
|
+
import { writeTaskProgress } from "./taskProgress.js";
|
|
8
|
+
import { writeTaskRuntime } from "./taskRuntime.js";
|
|
9
|
+
import { claimPendingTask, readTaskStatusFile, updateTaskStatusFile, } from "./taskStatusStore.js";
|
|
9
10
|
import { validateAssessmentFreshness } from "../assessments/assessmentStore.js";
|
|
10
11
|
import { buildAgentInvocation, buildExecutionPrompt } from "./agentInvocation.js";
|
|
11
12
|
import { buildChangeArtifacts, captureRepoSnapshot, compareSnapshots, emptyArtifactHygiene, writeSnapshot, extractExternalDirtyFiles, findNewExternalDirtyFiles, buildArtifactManifest, groupChangedFiles, } from "./changeCapture.js";
|
|
12
13
|
import { errorPayload } from "../errors.js";
|
|
13
|
-
import {
|
|
14
|
+
import { ARTIFACT_SCHEMA_VERSION } from "../version.js";
|
|
15
|
+
import { diagnoseAndroidBuild } from "../tools/workspace/androidDoctor.js";
|
|
14
16
|
import { runPostTaskCleanup } from "./postTaskCleanup.js";
|
|
17
|
+
import { atomicWriteFileSync, atomicWriteJsonFileSync } from "../utils/atomicFile.js";
|
|
18
|
+
import { allowedEnvironmentValues, buildChildEnvironment, redactProcessOutput, resolvePackageManagerInvocation, resolveTrustedExecutable, SecureProcessLogCapture, } from "./processSecurity.js";
|
|
15
19
|
const HEARTBEAT_INTERVAL_MS = 2000;
|
|
16
20
|
const GRACEFUL_KILL_MS = 2000;
|
|
17
21
|
const MAX_CAPTURE_CHARS = 100_000;
|
|
@@ -36,7 +40,24 @@ async function prepareTask(taskId) {
|
|
|
36
40
|
const statusFile = join(taskDir, "status.json");
|
|
37
41
|
if (!existsSync(statusFile))
|
|
38
42
|
throw new Error(`Task not found: "${taskId}"`);
|
|
39
|
-
const
|
|
43
|
+
const startedAtMs = Date.now();
|
|
44
|
+
const claim = claimPendingTask(statusFile, {
|
|
45
|
+
status: "running",
|
|
46
|
+
phase: "preparing",
|
|
47
|
+
started_at: new Date(startedAtMs).toISOString(),
|
|
48
|
+
last_heartbeat_at: new Date(startedAtMs).toISOString(),
|
|
49
|
+
current_command: null,
|
|
50
|
+
error: null,
|
|
51
|
+
});
|
|
52
|
+
if (!claim.claimed) {
|
|
53
|
+
const currentStatus = String(claim.status.status || "failed");
|
|
54
|
+
return {
|
|
55
|
+
task_id: taskId,
|
|
56
|
+
status: currentStatus,
|
|
57
|
+
error: `Task is ${currentStatus}; only pending tasks can be claimed for execution.`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const initialStatus = claim.status;
|
|
40
61
|
const planId = String(initialStatus.plan_id || "");
|
|
41
62
|
const agentName = String(initialStatus.agent || "");
|
|
42
63
|
const rawRepoPath = String(initialStatus.resolved_repo_path || initialStatus.repo_path || wsRoot);
|
|
@@ -50,7 +71,6 @@ async function prepareTask(taskId) {
|
|
|
50
71
|
catch (error) {
|
|
51
72
|
return failBeforeExecution(taskId, taskDir, errorMessage(error), error);
|
|
52
73
|
}
|
|
53
|
-
const startedAtMs = Date.now();
|
|
54
74
|
const deadlineMs = startedAtMs + timeoutSeconds * 1000;
|
|
55
75
|
let repoPath;
|
|
56
76
|
try {
|
|
@@ -82,15 +102,7 @@ async function prepareTask(taskId) {
|
|
|
82
102
|
return failBeforeExecution(taskId, taskDir, message, error);
|
|
83
103
|
}
|
|
84
104
|
}
|
|
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
|
-
});
|
|
105
|
+
updateStatus(taskDir, { timeout_seconds: timeoutSeconds });
|
|
94
106
|
writeTaskRuntime(taskDir, {
|
|
95
107
|
task_started_at: new Date(startedAtMs).toISOString(),
|
|
96
108
|
watcher_instance_id: process.env.PATCHWARDEN_WATCHER_INSTANCE_ID || undefined,
|
|
@@ -144,6 +156,8 @@ async function executeAgent(ctx) {
|
|
|
144
156
|
deadlineMs: ctx.deadlineMs,
|
|
145
157
|
stdoutPath: join(ctx.taskDir, "stdout.log"),
|
|
146
158
|
stderrPath: join(ctx.taskDir, "stderr.log"),
|
|
159
|
+
environmentVariableNames: invocation.environmentVariableNames,
|
|
160
|
+
blockedEnvironmentVariableNames: invocation.blockedEnvironmentVariableNames,
|
|
147
161
|
});
|
|
148
162
|
const agentResult = state.agentResult;
|
|
149
163
|
if (agentResult.terminationReason === "canceled" || agentResult.terminationReason === "killed") {
|
|
@@ -229,7 +243,7 @@ async function collectArtifacts(ctx, state) {
|
|
|
229
243
|
skipped: [{ path: ".", reason: "post_task_cleanup", skip_reason: errorMessage(error) }],
|
|
230
244
|
source_files_touched: 0,
|
|
231
245
|
};
|
|
232
|
-
|
|
246
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "post-task-cleanup.json"), cleanupReport);
|
|
233
247
|
updateStatus(ctx.taskDir, { cleanup: cleanupReport });
|
|
234
248
|
}
|
|
235
249
|
}
|
|
@@ -239,24 +253,16 @@ async function collectArtifacts(ctx, state) {
|
|
|
239
253
|
let artifactStatus = "collected";
|
|
240
254
|
let artifactCollectionError = null;
|
|
241
255
|
let timeoutHandle;
|
|
256
|
+
const artifactController = new AbortController();
|
|
242
257
|
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;
|
|
258
|
+
timeoutHandle = setTimeout(() => {
|
|
259
|
+
artifactController.abort(new Error("Artifact collection timed out"));
|
|
260
|
+
}, ARTIFACT_COLLECTION_TIMEOUT_MS);
|
|
261
|
+
const afterSnapshot = await captureRepoSnapshot(ctx.repoPath, artifactController.signal);
|
|
262
|
+
writeSnapshot(ctx.taskDir, "git-after.json", afterSnapshot);
|
|
263
|
+
changes = await buildChangeArtifacts(ctx.repoPath, ctx.beforeSnapshot, afterSnapshot, artifactController.signal);
|
|
256
264
|
}
|
|
257
265
|
catch (error) {
|
|
258
|
-
if (timeoutHandle)
|
|
259
|
-
clearTimeout(timeoutHandle);
|
|
260
266
|
state.lastCaughtError = error;
|
|
261
267
|
artifactCollectionError = errorMessage(error);
|
|
262
268
|
artifactStatus = artifactCollectionError.includes("timed out") ? "timeout" : "failed";
|
|
@@ -275,7 +281,7 @@ async function collectArtifacts(ctx, state) {
|
|
|
275
281
|
unavailable_reason: `Change capture failed: ${artifactCollectionError}`,
|
|
276
282
|
artifact_hygiene: emptyArtifactHygiene(),
|
|
277
283
|
};
|
|
278
|
-
|
|
284
|
+
atomicWriteFileSync(join(ctx.taskDir, "partial_result.md"), [
|
|
279
285
|
"# PatchWarden Partial Result",
|
|
280
286
|
"",
|
|
281
287
|
`Task: ${ctx.taskId}`,
|
|
@@ -295,7 +301,7 @@ async function collectArtifacts(ctx, state) {
|
|
|
295
301
|
"## Note",
|
|
296
302
|
"Artifact collection did not complete. The task result may be incomplete. Review stdout.log, stderr.log, and verify.json for details.",
|
|
297
303
|
"",
|
|
298
|
-
].join("\n")
|
|
304
|
+
].join("\n"));
|
|
299
305
|
if (state.finalStatus === "done_by_agent") {
|
|
300
306
|
state.finalError = `Change capture failed: ${artifactCollectionError}`;
|
|
301
307
|
state.finalStatus = "failed";
|
|
@@ -304,6 +310,10 @@ async function collectArtifacts(ctx, state) {
|
|
|
304
310
|
state.finalError ||= `Change capture failed: ${artifactCollectionError}`;
|
|
305
311
|
}
|
|
306
312
|
}
|
|
313
|
+
finally {
|
|
314
|
+
if (timeoutHandle)
|
|
315
|
+
clearTimeout(timeoutHandle);
|
|
316
|
+
}
|
|
307
317
|
const artifactCollectionFinishedAt = new Date().toISOString();
|
|
308
318
|
let outOfScopeChanges = [];
|
|
309
319
|
const preexistingExternalDirty = ctx.externalDirtyBaseline;
|
|
@@ -326,18 +336,18 @@ async function collectArtifacts(ctx, state) {
|
|
|
326
336
|
? [`Pre-existing external dirty files (not caused by this task): ${preexistingExternalDirty.length} file(s)`]
|
|
327
337
|
: [];
|
|
328
338
|
applyScopeViolationVerdict(ctx, state, changes, newOutOfScopeChanges, preexistingExternalDirty);
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
339
|
+
atomicWriteFileSync(join(ctx.taskDir, "git.diff"), changes.diff);
|
|
340
|
+
atomicWriteFileSync(join(ctx.taskDir, "diff.patch"), changes.diff);
|
|
341
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "changed-files.json"), { schema_version: ARTIFACT_SCHEMA_VERSION, ...changes });
|
|
332
342
|
const artifactManifest = await buildArtifactManifest(changes.changed_files, ctx.repoPath, ctx.taskId);
|
|
333
|
-
|
|
343
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "artifact_manifest.json"), artifactManifest);
|
|
334
344
|
const changedFileGroups = groupChangedFiles(changes.changed_files);
|
|
335
|
-
|
|
345
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "file-stats.json"), {
|
|
336
346
|
task_id: ctx.taskId,
|
|
337
347
|
additions: changes.additions,
|
|
338
348
|
deletions: changes.deletions,
|
|
339
349
|
files: changes.file_stats,
|
|
340
|
-
}
|
|
350
|
+
});
|
|
341
351
|
return {
|
|
342
352
|
changes, artifactStatus, artifactCollectionError, artifactCollectionStartedAt, artifactCollectionFinishedAt,
|
|
343
353
|
outOfScopeChanges, newOutOfScopeChanges, preexistingExternalDirty, preexistingWarnings, cleanupReport,
|
|
@@ -348,15 +358,15 @@ function applyScopeViolationVerdict(ctx, state, changes, newOutOfScopeChanges, p
|
|
|
348
358
|
if (newOutOfScopeChanges.length > 0) {
|
|
349
359
|
state.finalStatus = "failed_scope_violation";
|
|
350
360
|
state.finalError = `Detected ${newOutOfScopeChanges.length} new change(s) outside resolved_repo_path during task execution.`;
|
|
351
|
-
|
|
361
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "rollback-plan.json"), {
|
|
352
362
|
task_id: ctx.taskId,
|
|
353
363
|
status: "review_required",
|
|
354
364
|
automatic_rollback_performed: false,
|
|
355
365
|
warning: "Review ownership and concurrent edits before rollback. PatchWarden did not modify or restore these files.",
|
|
356
366
|
out_of_scope_changes: newOutOfScopeChanges,
|
|
357
367
|
preexisting_external_dirty_files: preexistingExternalDirty,
|
|
358
|
-
}
|
|
359
|
-
|
|
368
|
+
});
|
|
369
|
+
atomicWriteFileSync(join(ctx.taskDir, "rollback_scope_violation_plan.md"), [
|
|
360
370
|
"# Scope Violation Rollback Plan",
|
|
361
371
|
"",
|
|
362
372
|
`Task: ${ctx.taskId}`,
|
|
@@ -366,7 +376,11 @@ function applyScopeViolationVerdict(ctx, state, changes, newOutOfScopeChanges, p
|
|
|
366
376
|
"## New out-of-scope files (caused by this task)",
|
|
367
377
|
...newOutOfScopeChanges.map((file) => `- ${file.change}: ${file.path}`),
|
|
368
378
|
"",
|
|
369
|
-
].join("\n")
|
|
379
|
+
].join("\n"));
|
|
380
|
+
}
|
|
381
|
+
else if (changes.diff_redacted) {
|
|
382
|
+
state.finalStatus = "failed_policy_violation";
|
|
383
|
+
state.finalError = "Credential-like content was detected in the task diff. PatchWarden redacted the evidence; remove the sensitive content before retrying.";
|
|
370
384
|
}
|
|
371
385
|
else if (ctx.changePolicy === "no_changes" && changes.changed_files.length > 0) {
|
|
372
386
|
state.finalStatus = "failed_policy_violation";
|
|
@@ -375,7 +389,7 @@ function applyScopeViolationVerdict(ctx, state, changes, newOutOfScopeChanges, p
|
|
|
375
389
|
}
|
|
376
390
|
function finalizeTask(ctx, state, evidence) {
|
|
377
391
|
const { changes, artifactStatus, artifactCollectionError, artifactCollectionStartedAt, artifactCollectionFinishedAt, outOfScopeChanges, newOutOfScopeChanges, preexistingExternalDirty, preexistingWarnings, cleanupReport, artifactManifest, changedFileGroups } = evidence;
|
|
378
|
-
|
|
392
|
+
atomicWriteFileSync(join(ctx.taskDir, "test.log"), buildTestLog(state.testResult));
|
|
379
393
|
const verifyJson = buildVerifyJson(ctx.verifyCommands, state.verifyResults, ctx.repoPath);
|
|
380
394
|
if (newOutOfScopeChanges.length > 0) {
|
|
381
395
|
verifyJson.status = "failed";
|
|
@@ -383,10 +397,12 @@ function finalizeTask(ctx, state, evidence) {
|
|
|
383
397
|
}
|
|
384
398
|
else if (state.finalStatus === "failed_policy_violation") {
|
|
385
399
|
verifyJson.status = "failed";
|
|
386
|
-
verifyJson.failure_reason =
|
|
400
|
+
verifyJson.failure_reason = changes.diff_redacted
|
|
401
|
+
? "sensitive_content_violation"
|
|
402
|
+
: "change_policy_violation";
|
|
387
403
|
}
|
|
388
|
-
|
|
389
|
-
|
|
404
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "verify.json"), verifyJson);
|
|
405
|
+
atomicWriteFileSync(join(ctx.taskDir, "verify.log"), buildVerifyLog(verifyJson.commands));
|
|
390
406
|
if (!["canceled", "done_by_agent", "failed_verification", "failed_scope_violation", "failed_policy_violation"].includes(state.finalStatus))
|
|
391
407
|
state.finalStatus = "failed";
|
|
392
408
|
const finalPhase = state.finalStatus === "done_by_agent" ? "done_by_agent" : state.finalStatus;
|
|
@@ -422,17 +438,17 @@ function finalizeTask(ctx, state, evidence) {
|
|
|
422
438
|
androidWarning,
|
|
423
439
|
preexistingWarnings,
|
|
424
440
|
});
|
|
425
|
-
|
|
441
|
+
atomicWriteFileSync(join(ctx.taskDir, "result.md"), resultMd);
|
|
426
442
|
const resultJson = buildResultJson({
|
|
427
443
|
ctx, state, evidence, verifyJson, followup, androidDiagnostic, androidWarning,
|
|
428
444
|
});
|
|
429
|
-
|
|
445
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "result.json"), resultJson);
|
|
430
446
|
if (state.finalError) {
|
|
431
447
|
const structuredError = errorPayload(state.lastCaughtError);
|
|
432
|
-
|
|
448
|
+
atomicWriteJsonFileSync(join(ctx.taskDir, "error.log"), {
|
|
433
449
|
summary: state.finalError,
|
|
434
450
|
...structuredError,
|
|
435
|
-
}
|
|
451
|
+
});
|
|
436
452
|
}
|
|
437
453
|
const finishedAt = new Date().toISOString();
|
|
438
454
|
updateStatus(ctx.taskDir, {
|
|
@@ -456,6 +472,8 @@ function finalizeTask(ctx, state, evidence) {
|
|
|
456
472
|
verify_commands: ctx.verifyCommands,
|
|
457
473
|
diff_available: changes.diff_available,
|
|
458
474
|
diff_truncated: changes.diff_truncated,
|
|
475
|
+
diff_redacted: changes.diff_redacted === true,
|
|
476
|
+
diff_redaction_categories: changes.diff_redaction_categories ?? [],
|
|
459
477
|
workspace_dirty_before: changes.workspace_dirty_before,
|
|
460
478
|
workspace_dirty_after: changes.workspace_dirty_after,
|
|
461
479
|
workspace_dirty: changes.workspace_dirty_after,
|
|
@@ -478,6 +496,7 @@ function buildResultJson(input) {
|
|
|
478
496
|
const { ctx, state, evidence, verifyJson, followup, androidDiagnostic, androidWarning } = input;
|
|
479
497
|
const { changes, artifactStatus, artifactCollectionError, artifactCollectionStartedAt, artifactCollectionFinishedAt, outOfScopeChanges, newOutOfScopeChanges, preexistingExternalDirty, cleanupReport, artifactManifest, changedFileGroups } = evidence;
|
|
480
498
|
return {
|
|
499
|
+
schema_version: ARTIFACT_SCHEMA_VERSION,
|
|
481
500
|
task_id: ctx.taskId,
|
|
482
501
|
status: state.finalStatus,
|
|
483
502
|
agent: ctx.agentName,
|
|
@@ -527,6 +546,9 @@ function buildResultJson(input) {
|
|
|
527
546
|
warnings: [
|
|
528
547
|
...ctx.beforeSnapshot.warnings,
|
|
529
548
|
...(changes.diff_truncated ? ["diff.patch was truncated; changed-files.json retains file evidence."] : []),
|
|
549
|
+
...(changes.diff_redacted ? [
|
|
550
|
+
`Credential-like diff content was redacted (${(changes.diff_redaction_categories ?? []).join(", ") || "sensitive_content"}).`,
|
|
551
|
+
] : []),
|
|
530
552
|
...evidence.preexistingWarnings,
|
|
531
553
|
...(androidWarning ? [androidWarning] : []),
|
|
532
554
|
],
|
|
@@ -594,13 +616,22 @@ async function runManagedProcess(options) {
|
|
|
594
616
|
if (Date.now() >= options.deadlineMs) {
|
|
595
617
|
return { exitCode: null, stdout: "", stderr: "", spawnError: null, terminationReason: "timeout" };
|
|
596
618
|
}
|
|
619
|
+
const exactRedactionValues = allowedEnvironmentValues(options.environmentVariableNames);
|
|
620
|
+
const logCapture = new SecureProcessLogCapture([options.stdoutPath, options.stderrPath], options.maxLogBytes);
|
|
597
621
|
let child;
|
|
598
622
|
try {
|
|
599
|
-
|
|
623
|
+
const env = buildChildEnvironment({
|
|
624
|
+
cwd: options.cwd,
|
|
625
|
+
allowedNames: options.environmentVariableNames,
|
|
626
|
+
blockedNames: options.blockedEnvironmentVariableNames,
|
|
627
|
+
});
|
|
628
|
+
const command = resolveTrustedExecutable(options.command, options.cwd, { pathValue: env.PATH });
|
|
629
|
+
child = spawn(command, options.args, {
|
|
600
630
|
cwd: options.cwd,
|
|
601
631
|
stdio: ["ignore", "pipe", "pipe"],
|
|
602
632
|
detached: process.platform !== "win32",
|
|
603
633
|
windowsHide: true,
|
|
634
|
+
env,
|
|
604
635
|
});
|
|
605
636
|
}
|
|
606
637
|
catch (error) {
|
|
@@ -612,8 +643,6 @@ async function runManagedProcess(options) {
|
|
|
612
643
|
child_pid: child.pid,
|
|
613
644
|
child_started_at: new Date().toISOString(),
|
|
614
645
|
});
|
|
615
|
-
const stdoutStream = openStream(options.stdoutPath);
|
|
616
|
-
const stderrStream = openStream(options.stderrPath);
|
|
617
646
|
let stdout = "";
|
|
618
647
|
let stderr = "";
|
|
619
648
|
let spawnError = null;
|
|
@@ -680,12 +709,12 @@ async function runManagedProcess(options) {
|
|
|
680
709
|
child.stdout?.on("data", (chunk) => {
|
|
681
710
|
const text = chunk.toString("utf-8");
|
|
682
711
|
stdout = appendBounded(stdout, text);
|
|
683
|
-
|
|
712
|
+
logCapture.append(options.stdoutPath, chunk);
|
|
684
713
|
});
|
|
685
714
|
child.stderr?.on("data", (chunk) => {
|
|
686
715
|
const text = chunk.toString("utf-8");
|
|
687
716
|
stderr = appendBounded(stderr, text);
|
|
688
|
-
|
|
717
|
+
logCapture.append(options.stderrPath, chunk);
|
|
689
718
|
});
|
|
690
719
|
heartbeat();
|
|
691
720
|
const heartbeatTimer = setInterval(heartbeat, HEARTBEAT_INTERVAL_MS);
|
|
@@ -702,12 +731,6 @@ async function runManagedProcess(options) {
|
|
|
702
731
|
};
|
|
703
732
|
childExitFinish = finish;
|
|
704
733
|
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
734
|
child.once("error", (error) => {
|
|
712
735
|
spawnError = error.message;
|
|
713
736
|
finish(null);
|
|
@@ -716,8 +739,7 @@ async function runManagedProcess(options) {
|
|
|
716
739
|
clearInterval(heartbeatTimer);
|
|
717
740
|
if (forceTimer)
|
|
718
741
|
clearTimeout(forceTimer);
|
|
719
|
-
|
|
720
|
-
stderrStream?.destroy();
|
|
742
|
+
logCapture.flush(exactRedactionValues);
|
|
721
743
|
writeTaskRuntime(options.taskDir, {
|
|
722
744
|
phase: options.phase,
|
|
723
745
|
last_heartbeat_at: new Date().toISOString(),
|
|
@@ -725,7 +747,15 @@ async function runManagedProcess(options) {
|
|
|
725
747
|
runner_pid: process.pid,
|
|
726
748
|
child_pid: undefined,
|
|
727
749
|
});
|
|
728
|
-
|
|
750
|
+
const safeStdout = redactProcessOutput(stdout, exactRedactionValues);
|
|
751
|
+
const safeStderr = redactProcessOutput(stderr, exactRedactionValues);
|
|
752
|
+
return {
|
|
753
|
+
exitCode,
|
|
754
|
+
stdout: safeStdout.length > MAX_CAPTURE_CHARS ? safeStdout.slice(-MAX_CAPTURE_CHARS) : safeStdout,
|
|
755
|
+
stderr: safeStderr.length > MAX_CAPTURE_CHARS ? safeStderr.slice(-MAX_CAPTURE_CHARS) : safeStderr,
|
|
756
|
+
spawnError,
|
|
757
|
+
terminationReason,
|
|
758
|
+
};
|
|
729
759
|
}
|
|
730
760
|
async function runTrustedTestCommand(testCommand, repoPath, taskDir, statusFile, deadlineMs) {
|
|
731
761
|
const config = getConfig();
|
|
@@ -736,9 +766,9 @@ async function runTrustedTestCommand(testCommand, repoPath, taskDir, statusFile,
|
|
|
736
766
|
let command = parts[0];
|
|
737
767
|
let args = parts.slice(1);
|
|
738
768
|
if (process.platform === "win32" && /^(npm|npm\.cmd|pnpm|pnpm\.cmd)$/i.test(command)) {
|
|
739
|
-
const
|
|
740
|
-
command =
|
|
741
|
-
args = [
|
|
769
|
+
const packageManager = resolvePackageManagerInvocation(command, repoPath);
|
|
770
|
+
command = packageManager.command;
|
|
771
|
+
args = [...packageManager.argsPrefix, ...args];
|
|
742
772
|
}
|
|
743
773
|
const result = await runManagedProcess({
|
|
744
774
|
command,
|
|
@@ -963,10 +993,10 @@ function normalizeTimeout(value, config) {
|
|
|
963
993
|
function failBeforeExecution(taskId, taskDir, message, caughtError) {
|
|
964
994
|
// Phase 9: Preserve PatchWardenError structured info in error.log
|
|
965
995
|
const structuredError = caughtError ? errorPayload(caughtError) : { error: message };
|
|
966
|
-
|
|
996
|
+
atomicWriteJsonFileSync(join(taskDir, "error.log"), {
|
|
967
997
|
summary: message,
|
|
968
998
|
...structuredError,
|
|
969
|
-
}
|
|
999
|
+
});
|
|
970
1000
|
const current = readStatus(join(taskDir, "status.json"));
|
|
971
1001
|
const now = new Date().toISOString();
|
|
972
1002
|
const verify = {
|
|
@@ -974,17 +1004,17 @@ function failBeforeExecution(taskId, taskDir, message, caughtError) {
|
|
|
974
1004
|
requested_commands: Array.isArray(current.verify_commands) ? current.verify_commands : [],
|
|
975
1005
|
commands: [],
|
|
976
1006
|
};
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
1007
|
+
atomicWriteJsonFileSync(join(taskDir, "verify.json"), verify);
|
|
1008
|
+
atomicWriteFileSync(join(taskDir, "verify.log"), `Verification did not run: ${message}\n`);
|
|
1009
|
+
atomicWriteFileSync(join(taskDir, "test.log"), `(not run)\nExit code: not run\n${message}\n`);
|
|
1010
|
+
atomicWriteFileSync(join(taskDir, "git.diff"), "(task failed before change capture)\n");
|
|
1011
|
+
atomicWriteFileSync(join(taskDir, "diff.patch"), "(task failed before change capture)\n");
|
|
1012
|
+
atomicWriteJsonFileSync(join(taskDir, "file-stats.json"), {
|
|
983
1013
|
task_id: taskId,
|
|
984
1014
|
additions: 0,
|
|
985
1015
|
deletions: 0,
|
|
986
1016
|
files: [],
|
|
987
|
-
}
|
|
1017
|
+
});
|
|
988
1018
|
const result = {
|
|
989
1019
|
task_id: taskId,
|
|
990
1020
|
status: "failed",
|
|
@@ -1010,8 +1040,8 @@ function failBeforeExecution(taskId, taskDir, message, caughtError) {
|
|
|
1010
1040
|
safe_followup_prompt: "Inspect result.json and error.log, correct the task metadata or configuration, and retry without widening repository scope.",
|
|
1011
1041
|
next_steps: ["Fix task metadata or configuration and retry the task."],
|
|
1012
1042
|
};
|
|
1013
|
-
|
|
1014
|
-
|
|
1043
|
+
atomicWriteJsonFileSync(join(taskDir, "result.json"), result);
|
|
1044
|
+
atomicWriteFileSync(join(taskDir, "result.md"), `# PatchWarden Task Result\n\n## Status\nfailed\n\n## Summary\n${message}\n`);
|
|
1015
1045
|
updateStatus(taskDir, {
|
|
1016
1046
|
status: "failed",
|
|
1017
1047
|
phase: "failed",
|
|
@@ -1037,12 +1067,10 @@ function setTaskPhase(taskDir, phase, currentCommand, note) {
|
|
|
1037
1067
|
}
|
|
1038
1068
|
function updateStatus(taskDir, patch) {
|
|
1039
1069
|
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");
|
|
1070
|
+
updateTaskStatusFile(statusFile, patch);
|
|
1043
1071
|
}
|
|
1044
1072
|
function readStatus(statusFile) {
|
|
1045
|
-
return
|
|
1073
|
+
return readTaskStatusFile(statusFile);
|
|
1046
1074
|
}
|
|
1047
1075
|
function gracefulKill(child) {
|
|
1048
1076
|
try {
|
|
@@ -1058,10 +1086,13 @@ function forceKill(child) {
|
|
|
1058
1086
|
return;
|
|
1059
1087
|
try {
|
|
1060
1088
|
if (process.platform === "win32") {
|
|
1061
|
-
const
|
|
1089
|
+
const systemRoot = process.env.SystemRoot || process.env.WINDIR || "C:\\Windows";
|
|
1090
|
+
const taskkill = resolveTrustedExecutable(`${systemRoot}\\System32\\taskkill.exe`, process.cwd());
|
|
1091
|
+
const result = spawnSync(taskkill, ["/PID", String(child.pid), "/T", "/F"], {
|
|
1062
1092
|
stdio: "ignore",
|
|
1063
1093
|
timeout: 5000,
|
|
1064
1094
|
windowsHide: true,
|
|
1095
|
+
env: buildChildEnvironment({ cwd: process.cwd() }),
|
|
1065
1096
|
});
|
|
1066
1097
|
if (result.status !== 0)
|
|
1067
1098
|
child.kill("SIGKILL");
|
|
@@ -1077,9 +1108,6 @@ function forceKill(child) {
|
|
|
1077
1108
|
catch { } // cleanup failure is safe to ignore
|
|
1078
1109
|
}
|
|
1079
1110
|
}
|
|
1080
|
-
function openStream(path) {
|
|
1081
|
-
return path ? createWriteStream(path, { flags: "a" }) : null;
|
|
1082
|
-
}
|
|
1083
1111
|
function appendBounded(current, next) {
|
|
1084
1112
|
const combined = current + next;
|
|
1085
1113
|
return combined.length <= MAX_CAPTURE_CHARS
|
|
@@ -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;
|