patchwarden 1.5.1 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PatchWarden.cmd +32 -2
- package/README.en.md +158 -26
- package/README.md +124 -24
- package/dist/assessments/agentAssessor.js +21 -12
- package/dist/assessments/assessmentDiagnostics.d.ts +29 -0
- package/dist/assessments/assessmentDiagnostics.js +132 -0
- package/dist/assessments/assessmentStore.d.ts +39 -0
- package/dist/assessments/assessmentStore.js +207 -45
- package/dist/assessments/securitySnapshot.d.ts +50 -0
- package/dist/assessments/securitySnapshot.js +158 -0
- package/dist/config.d.ts +15 -0
- package/dist/config.js +66 -13
- package/dist/control/fileManager.d.ts +2 -0
- package/dist/control/fileManager.js +39 -0
- package/dist/control/middleware/auth.d.ts +6 -0
- package/dist/control/middleware/auth.js +14 -5
- package/dist/control/middleware/static.js +16 -3
- package/dist/control/routeTable.d.ts +24 -0
- package/dist/control/routeTable.js +263 -0
- package/dist/control/routes/audit.js +17 -7
- package/dist/control/routes/evidence.js +1 -1
- package/dist/control/routes/lineage.js +1 -1
- package/dist/control/routes/process.d.ts +7 -0
- package/dist/control/routes/process.js +329 -42
- package/dist/control/routes/sessions.d.ts +9 -1
- package/dist/control/routes/sessions.js +84 -21
- package/dist/control/routes/status.d.ts +60 -1
- package/dist/control/routes/status.js +94 -15
- package/dist/control/routes/taskActions.js +24 -60
- package/dist/control/routes/tasks.d.ts +4 -0
- package/dist/control/routes/tasks.js +61 -62
- package/dist/control/routes/workspace.js +50 -23
- package/dist/control/runtime.d.ts +3 -2
- package/dist/control/runtime.js +65 -40
- package/dist/control/server.js +37 -365
- package/dist/control/shared.d.ts +25 -1
- package/dist/control/shared.js +64 -11
- package/dist/diagnostics/allowedTestCommandSafety.d.ts +1 -0
- package/dist/diagnostics/allowedTestCommandSafety.js +11 -0
- package/dist/direct/directAudit.js +234 -225
- package/dist/direct/directGuards.d.ts +2 -1
- package/dist/direct/directGuards.js +42 -29
- package/dist/direct/directPatch.d.ts +6 -1
- package/dist/direct/directPatch.js +55 -8
- package/dist/direct/directSessionStore.d.ts +16 -6
- package/dist/direct/directSessionStore.js +128 -35
- package/dist/direct/directVerification.js +8 -16
- package/dist/doctor.js +65 -57
- package/dist/goal/acceptanceEngine.d.ts +1 -1
- package/dist/goal/goalProgress.js +75 -69
- package/dist/goal/goalReport.js +5 -12
- package/dist/goal/goalStatus.d.ts +2 -0
- package/dist/goal/goalStatus.js +10 -3
- package/dist/goal/goalStore.d.ts +9 -2
- package/dist/goal/goalStore.js +152 -45
- package/dist/goal/handoffExport.js +6 -6
- package/dist/goal/specKitImport.js +97 -96
- package/dist/goal/subgoalSync.js +11 -9
- package/dist/goal/worktreeManager.d.ts +2 -0
- package/dist/goal/worktreeManager.js +209 -136
- package/dist/httpServer.js +111 -32
- package/dist/logging.js +5 -3
- package/dist/policy/projectPolicy.js +5 -2
- package/dist/release/releaseGate.d.ts +1 -1
- package/dist/release/releaseGate.js +78 -25
- package/dist/runner/agentInvocation.d.ts +14 -0
- package/dist/runner/agentInvocation.js +96 -4
- package/dist/runner/changeCapture.d.ts +13 -5
- package/dist/runner/changeCapture.js +243 -61
- package/dist/runner/postTaskCleanup.js +48 -19
- package/dist/runner/processSecurity.d.ts +56 -0
- package/dist/runner/processSecurity.js +373 -0
- package/dist/runner/runTask.d.ts +2 -1
- package/dist/runner/runTask.js +162 -93
- package/dist/runner/simpleProcess.d.ts +3 -0
- package/dist/runner/simpleProcess.js +50 -36
- package/dist/{taskProgress.d.ts → runner/taskProgress.d.ts} +1 -1
- package/dist/{taskProgress.js → runner/taskProgress.js} +2 -2
- package/dist/{taskRuntime.d.ts → runner/taskRuntime.d.ts} +3 -1
- package/dist/{taskRuntime.js → runner/taskRuntime.js} +3 -2
- package/dist/runner/taskStatusStore.d.ts +11 -0
- package/dist/runner/taskStatusStore.js +35 -0
- package/dist/runner/watch.d.ts +14 -1
- package/dist/runner/watch.js +320 -49
- package/dist/security/contentRedaction.js +2 -2
- package/dist/security/discoveryTokenStore.d.ts +3 -2
- package/dist/security/discoveryTokenStore.js +17 -2
- package/dist/security/loopbackHost.d.ts +2 -0
- package/dist/security/loopbackHost.js +16 -0
- package/dist/security/pathGuard.js +32 -17
- package/dist/security/planGuard.js +2 -1
- package/dist/security/runtimeGuard.js +9 -6
- package/dist/security/secretComparison.d.ts +3 -0
- package/dist/security/secretComparison.js +11 -0
- package/dist/security/sensitiveGuard.d.ts +5 -0
- package/dist/security/sensitiveGuard.js +27 -8
- package/dist/security/toolInvocationGuard.d.ts +2 -2
- package/dist/security/toolInvocationGuard.js +1 -1
- package/dist/security/workspaceRootGuard.d.ts +8 -0
- package/dist/security/workspaceRootGuard.js +47 -0
- package/dist/smoke-test.js +27 -26
- package/dist/tools/catalog/index.d.ts +4 -0
- package/dist/tools/catalog/index.js +4 -0
- package/dist/tools/{toolCatalog.js → catalog/toolCatalog.js} +3 -12
- package/dist/tools/{toolRegistry.d.ts → catalog/toolRegistry.d.ts} +3 -3
- package/dist/tools/{toolRegistry.js → catalog/toolRegistry.js} +6 -15
- package/dist/tools/definitions/toolDefs.d.ts +18 -0
- package/dist/tools/definitions/toolDefs.js +1177 -0
- package/dist/tools/{auditSession.d.ts → diagnostics/auditSession.d.ts} +1 -1
- package/dist/tools/{auditSession.js → diagnostics/auditSession.js} +2 -2
- package/dist/tools/{auditTask.d.ts → diagnostics/auditTask.d.ts} +2 -1
- package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +134 -36
- package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +18 -6
- package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +36 -8
- package/dist/tools/diagnostics/index.d.ts +6 -0
- package/dist/tools/diagnostics/index.js +6 -0
- package/dist/tools/{safeStatus.d.ts → diagnostics/safeStatus.d.ts} +3 -3
- package/dist/tools/{safeStatus.js → diagnostics/safeStatus.js} +6 -6
- package/dist/tools/{safeViews.d.ts → diagnostics/safeViews.d.ts} +12 -8
- package/dist/tools/{safeViews.js → diagnostics/safeViews.js} +23 -13
- package/dist/tools/{schemaDriftCheck.d.ts → diagnostics/schemaDriftCheck.d.ts} +1 -1
- package/dist/tools/{schemaDriftCheck.js → diagnostics/schemaDriftCheck.js} +2 -2
- package/dist/tools/{createDirectSession.js → direct/createDirectSession.js} +6 -6
- package/dist/tools/{finalizeDirectSession.d.ts → direct/finalizeDirectSession.d.ts} +1 -1
- package/dist/tools/direct/finalizeDirectSession.js +86 -0
- package/dist/tools/direct/index.d.ts +3 -0
- package/dist/tools/direct/index.js +3 -0
- package/dist/tools/{runDirectVerificationBundle.js → direct/runDirectVerificationBundle.js} +2 -2
- package/dist/tools/{discoverTools.d.ts → discovery/discoverTools.d.ts} +2 -2
- package/dist/tools/{discoverTools.js → discovery/discoverTools.js} +5 -5
- package/dist/tools/{explainTool.d.ts → discovery/explainTool.d.ts} +2 -2
- package/dist/tools/{explainTool.js → discovery/explainTool.js} +2 -2
- package/dist/tools/discovery/index.d.ts +3 -0
- package/dist/tools/discovery/index.js +3 -0
- package/dist/tools/{invokeDiscoveredTool.d.ts → discovery/invokeDiscoveredTool.d.ts} +2 -2
- package/dist/tools/{invokeDiscoveredTool.js → discovery/invokeDiscoveredTool.js} +5 -5
- package/dist/tools/dispatch/coreDispatch.js +32 -31
- package/dist/tools/dispatch/diagnosticDispatch.d.ts +3 -5
- package/dist/tools/dispatch/diagnosticDispatch.js +70 -68
- package/dist/tools/dispatch/directDispatch.js +11 -10
- package/dist/tools/dispatch/goalDispatch.js +5 -4
- package/dist/tools/dispatch/releaseDispatch.js +3 -2
- package/dist/tools/dispatch/validation.d.ts +7 -0
- package/dist/tools/dispatch/validation.js +71 -0
- package/dist/tools/{getPlan.js → goals/getPlan.js} +4 -4
- package/dist/tools/{goalSubgoalTask.d.ts → goals/goalSubgoalTask.d.ts} +1 -1
- package/dist/tools/goals/goalSubgoalTask.js +118 -0
- package/dist/tools/goals/index.d.ts +3 -0
- package/dist/tools/goals/index.js +3 -0
- package/dist/tools/{savePlan.js → goals/savePlan.js} +28 -8
- package/dist/tools/registry.d.ts +7 -11
- package/dist/tools/registry.js +17 -1178
- package/dist/tools/{checkReleaseGate.d.ts → release/checkReleaseGate.d.ts} +1 -1
- package/dist/tools/{checkReleaseGate.js → release/checkReleaseGate.js} +3 -3
- package/dist/tools/release/index.d.ts +2 -0
- package/dist/tools/release/index.js +2 -0
- package/dist/tools/{releaseMode.d.ts → release/releaseMode.d.ts} +2 -2
- package/dist/tools/{releaseMode.js → release/releaseMode.js} +46 -26
- package/dist/tools/tasks/cancelTask.d.ts +11 -0
- package/dist/tools/tasks/cancelTask.js +86 -0
- package/dist/tools/{createTask.d.ts → tasks/createTask.d.ts} +12 -4
- package/dist/tools/{createTask.js → tasks/createTask.js} +110 -34
- package/dist/tools/{diagnoseTask.d.ts → tasks/diagnoseTask.d.ts} +1 -1
- package/dist/tools/{diagnoseTask.js → tasks/diagnoseTask.js} +12 -16
- package/dist/tools/{evidencePack.js → tasks/evidencePack.js} +34 -22
- package/dist/tools/{getTaskFile.js → tasks/getTaskFile.js} +4 -4
- package/dist/tools/{getTaskStatus.d.ts → tasks/getTaskStatus.d.ts} +6 -1
- package/dist/tools/{getTaskStatus.js → tasks/getTaskStatus.js} +11 -6
- package/dist/tools/{getTaskStdoutTail.js → tasks/getTaskStdoutTail.js} +13 -11
- package/dist/tools/{getTaskSummary.js → tasks/getTaskSummary.js} +35 -36
- package/dist/tools/tasks/index.d.ts +18 -0
- package/dist/tools/tasks/index.js +18 -0
- package/dist/tools/tasks/killTask.d.ts +1 -0
- package/dist/tools/{listTasks.d.ts → tasks/listTasks.d.ts} +1 -1
- package/dist/tools/{listTasks.js → tasks/listTasks.js} +5 -4
- package/dist/tools/{reconcileTasks.d.ts → tasks/reconcileTasks.d.ts} +3 -2
- package/dist/tools/tasks/reconcileTasks.js +496 -0
- package/dist/tools/tasks/retryTask.js +60 -0
- package/dist/tools/{runTaskLoop.d.ts → tasks/runTaskLoop.d.ts} +6 -6
- package/dist/tools/{runTaskLoop.js → tasks/runTaskLoop.js} +63 -41
- package/dist/tools/tasks/runVerification.js +38 -0
- package/dist/tools/{taskLineage.js → tasks/taskLineage.js} +8 -7
- package/dist/tools/{taskOutputs.d.ts → tasks/taskOutputs.d.ts} +2 -2
- package/dist/tools/{taskOutputs.js → tasks/taskOutputs.js} +17 -16
- package/dist/tools/tasks/taskStates.d.ts +6 -0
- package/dist/tools/tasks/taskStates.js +52 -0
- package/dist/tools/{waitForTask.js → tasks/waitForTask.js} +3 -11
- package/dist/tools/{androidDoctor.js → workspace/androidDoctor.js} +16 -10
- package/dist/tools/{applyPatch.d.ts → workspace/applyPatch.d.ts} +1 -1
- package/dist/tools/workspace/applyPatch.js +53 -0
- package/dist/tools/{discardWorktree.js → workspace/discardWorktree.js} +3 -3
- package/dist/tools/workspace/index.d.ts +10 -0
- package/dist/tools/workspace/index.js +10 -0
- package/dist/tools/workspace/listAgents.d.ts +23 -0
- package/dist/tools/workspace/listAgents.js +56 -0
- package/dist/tools/{listWorkspace.js → workspace/listWorkspace.js} +3 -3
- package/dist/tools/{mergeWorktree.js → workspace/mergeWorktree.js} +3 -3
- package/dist/tools/{readWorkspaceFile.js → workspace/readWorkspaceFile.js} +8 -8
- package/dist/tools/{recommendAgentForTask.js → workspace/recommendAgentForTask.js} +3 -3
- package/dist/tools/{searchWorkspace.js → workspace/searchWorkspace.js} +5 -5
- package/dist/tools/{syncFile.d.ts → workspace/syncFile.d.ts} +1 -1
- package/dist/tools/workspace/syncFile.js +134 -0
- package/dist/utils/atomicFile.d.ts +7 -0
- package/dist/utils/atomicFile.js +67 -0
- package/dist/utils/boundedFile.d.ts +8 -0
- package/dist/utils/boundedFile.js +93 -0
- package/dist/utils/lockedJsonFile.d.ts +15 -0
- package/dist/utils/lockedJsonFile.js +224 -0
- package/dist/utils/platform.d.ts +11 -0
- package/dist/utils/platform.js +26 -0
- package/dist/utils/stableJson.d.ts +2 -0
- package/dist/utils/stableJson.js +12 -0
- package/dist/version.d.ts +3 -2
- package/dist/version.js +3 -2
- package/dist/watcherStatus.d.ts +2 -2
- package/dist/watcherStatus.js +18 -0
- package/docs/CODE_WIKI.md +808 -143
- package/docs/architecture.md +15 -55
- package/docs/assets/patchwarden-desktop-onboarding.png +0 -0
- package/docs/control-center/README.md +4 -4
- package/docs/dashboard-overview.md +4 -4
- package/docs/desktop-app.md +175 -0
- package/docs/evidence-pack-schema.md +3 -3
- package/docs/open-source-application.md +16 -11
- package/docs/release-checklist.md +1 -1
- package/docs/release-evidence.md +35 -39
- package/docs/task-safe-review-workflow.md +5 -5
- package/docs/threat-model.md +19 -1
- package/examples/config.example.json +4 -2
- package/package.json +27 -10
- package/scripts/README.md +15 -6
- package/scripts/build.js +11 -0
- package/scripts/checks/brand-check.js +3 -0
- package/scripts/checks/build-output-check.js +37 -0
- package/scripts/checks/control-center-smoke.js +102 -10
- package/scripts/checks/control-smoke.js +19 -8
- package/scripts/checks/http-mcp-smoke.js +82 -4
- package/scripts/checks/lifecycle-smoke.js +15 -12
- package/scripts/checks/mcp-manifest-check.js +1 -1
- package/scripts/checks/mcp-smoke.js +58 -4
- package/scripts/checks/package-manifest-check.js +20 -7
- package/scripts/checks/unit-tests.js +3 -5
- package/scripts/checks/watcher-supervisor-smoke.js +38 -1
- package/scripts/control/get-patchwarden-health.ps1 +1 -1
- package/scripts/control/manage-patchwarden.ps1 +69 -4
- package/scripts/control/provision-patchwarden-tunnel.ps1 +127 -0
- package/scripts/control/run-background-supervisor.ps1 +41 -0
- package/scripts/control/start-patchwarden-tunnel.ps1 +191 -33
- package/scripts/e2e/demo-runtime.mjs +153 -0
- package/{PatchWarden-Control-Tray.cmd → scripts/launchers/PatchWarden-Control-Tray.cmd} +3 -3
- package/scripts/launchers/PatchWarden-Control.cmd +6 -0
- package/{PatchWarden-Desktop.cmd → scripts/launchers/PatchWarden-Desktop.cmd} +2 -2
- package/scripts/launchers/Restart-PatchWarden-Control.cmd +6 -0
- package/{Stop-PatchWarden.cmd → scripts/launchers/Stop-PatchWarden.cmd} +2 -2
- package/scripts/lib/clean-generated-output.js +11 -0
- package/scripts/lib/file-discovery.js +12 -0
- package/scripts/release/desktop-preflight.js +211 -0
- package/scripts/release/pack-clean.js +59 -14
- package/src/assessments/agentAssessor.ts +26 -12
- package/src/assessments/assessmentDiagnostics.ts +172 -0
- package/src/assessments/assessmentStore.ts +288 -51
- package/src/assessments/securitySnapshot.ts +231 -0
- package/src/config.ts +70 -13
- package/src/control/fileManager.ts +40 -0
- package/src/control/middleware/auth.ts +22 -4
- package/src/control/middleware/static.ts +16 -3
- package/src/control/routeTable.ts +301 -0
- package/src/control/routes/audit.ts +14 -6
- package/src/control/routes/evidence.ts +1 -1
- package/src/control/routes/lineage.ts +1 -1
- package/src/control/routes/process.ts +341 -40
- package/src/control/routes/sessions.ts +88 -25
- package/src/control/routes/status.ts +134 -17
- package/src/control/routes/taskActions.ts +24 -58
- package/src/control/routes/tasks.ts +66 -58
- package/src/control/routes/workspace.ts +56 -25
- package/src/control/runtime.ts +71 -43
- package/src/control/server.ts +37 -342
- package/src/control/shared.ts +77 -11
- package/src/diagnostics/allowedTestCommandSafety.ts +12 -0
- package/src/direct/directAudit.ts +17 -9
- package/src/direct/directGuards.ts +72 -44
- package/src/direct/directPatch.ts +95 -8
- package/src/direct/directSessionStore.ts +213 -55
- package/src/direct/directVerification.ts +9 -23
- package/src/doctor.ts +76 -64
- package/src/goal/acceptanceEngine.ts +1 -1
- package/src/goal/goalProgress.ts +17 -13
- package/src/goal/goalReport.ts +5 -13
- package/src/goal/goalStatus.ts +19 -3
- package/src/goal/goalStore.ts +241 -54
- package/src/goal/handoffExport.ts +6 -6
- package/src/goal/specKitImport.ts +12 -12
- package/src/goal/subgoalSync.ts +11 -8
- package/src/goal/worktreeManager.ts +139 -22
- package/src/httpServer.ts +113 -30
- package/src/logging.ts +10 -3
- package/src/policy/projectPolicy.ts +5 -2
- package/src/release/releaseGate.ts +74 -27
- package/src/runner/agentInvocation.ts +126 -4
- package/src/runner/changeCapture.ts +291 -57
- package/src/runner/postTaskCleanup.ts +41 -17
- package/src/runner/processSecurity.ts +433 -0
- package/src/runner/runTask.ts +197 -97
- package/src/runner/simpleProcess.ts +66 -31
- package/src/{taskProgress.ts → runner/taskProgress.ts} +3 -3
- package/src/{taskRuntime.ts → runner/taskRuntime.ts} +6 -3
- package/src/runner/taskStatusStore.ts +69 -0
- package/src/runner/watch.ts +348 -52
- package/src/security/contentRedaction.ts +2 -2
- package/src/security/discoveryTokenStore.ts +18 -4
- package/src/security/loopbackHost.ts +19 -0
- package/src/security/pathGuard.ts +35 -16
- package/src/security/planGuard.ts +2 -1
- package/src/security/runtimeGuard.ts +9 -6
- package/src/security/secretComparison.ts +13 -0
- package/src/security/sensitiveGuard.ts +27 -8
- package/src/security/toolInvocationGuard.ts +2 -2
- package/src/security/workspaceRootGuard.ts +53 -0
- package/src/smoke-test.ts +27 -26
- package/src/tools/catalog/index.ts +4 -0
- package/src/tools/{toolCatalog.ts → catalog/toolCatalog.ts} +3 -12
- package/src/tools/{toolRegistry.ts → catalog/toolRegistry.ts} +7 -16
- package/src/tools/definitions/toolDefs.ts +1264 -0
- package/src/tools/{auditSession.ts → diagnostics/auditSession.ts} +2 -2
- package/src/tools/{auditTask.ts → diagnostics/auditTask.ts} +136 -39
- package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +37 -9
- package/src/tools/diagnostics/index.ts +6 -0
- package/src/tools/{safeStatus.ts → diagnostics/safeStatus.ts} +7 -7
- package/src/tools/{safeViews.ts → diagnostics/safeViews.ts} +27 -17
- package/src/tools/{schemaDriftCheck.ts → diagnostics/schemaDriftCheck.ts} +3 -3
- package/src/tools/{createDirectSession.ts → direct/createDirectSession.ts} +6 -6
- package/src/tools/{finalizeDirectSession.ts → direct/finalizeDirectSession.ts} +7 -5
- package/src/tools/direct/index.ts +3 -0
- package/src/tools/{runDirectVerificationBundle.ts → direct/runDirectVerificationBundle.ts} +2 -2
- package/src/tools/{discoverTools.ts → discovery/discoverTools.ts} +6 -6
- package/src/tools/{explainTool.ts → discovery/explainTool.ts} +3 -3
- package/src/tools/discovery/index.ts +3 -0
- package/src/tools/{invokeDiscoveredTool.ts → discovery/invokeDiscoveredTool.ts} +7 -7
- package/src/tools/dispatch/coreDispatch.ts +36 -31
- package/src/tools/dispatch/diagnosticDispatch.ts +26 -21
- package/src/tools/dispatch/directDispatch.ts +11 -10
- package/src/tools/dispatch/goalDispatch.ts +5 -4
- package/src/tools/dispatch/releaseDispatch.ts +3 -2
- package/src/tools/dispatch/validation.ts +78 -0
- package/src/tools/{getPlan.ts → goals/getPlan.ts} +4 -4
- package/src/tools/{goalSubgoalTask.ts → goals/goalSubgoalTask.ts} +41 -26
- package/src/tools/goals/index.ts +3 -0
- package/src/tools/{savePlan.ts → goals/savePlan.ts} +38 -8
- package/src/tools/registry.ts +17 -1261
- package/src/tools/{checkReleaseGate.ts → release/checkReleaseGate.ts} +3 -3
- package/src/tools/release/index.ts +2 -0
- package/src/tools/{releaseMode.ts → release/releaseMode.ts} +42 -24
- package/src/tools/tasks/cancelTask.ts +110 -0
- package/src/tools/{createTask.ts → tasks/createTask.ts} +161 -33
- package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +12 -16
- package/src/tools/{evidencePack.ts → tasks/evidencePack.ts} +36 -22
- package/src/tools/{getTaskFile.ts → tasks/getTaskFile.ts} +4 -4
- package/src/tools/{getTaskStatus.ts → tasks/getTaskStatus.ts} +13 -6
- package/src/tools/{getTaskStdoutTail.ts → tasks/getTaskStdoutTail.ts} +13 -11
- package/src/tools/{getTaskSummary.ts → tasks/getTaskSummary.ts} +46 -44
- package/src/tools/tasks/index.ts +18 -0
- package/src/tools/{listTasks.ts → tasks/listTasks.ts} +6 -5
- package/src/tools/{reconcileTasks.ts → tasks/reconcileTasks.ts} +228 -77
- package/src/tools/tasks/retryTask.ts +69 -0
- package/src/tools/{runTaskLoop.ts → tasks/runTaskLoop.ts} +73 -46
- package/src/tools/{runVerification.ts → tasks/runVerification.ts} +6 -3
- package/src/tools/{taskLineage.ts → tasks/taskLineage.ts} +8 -7
- package/src/tools/{taskOutputs.ts → tasks/taskOutputs.ts} +27 -20
- package/src/tools/tasks/taskStates.ts +54 -0
- package/src/tools/{waitForTask.ts → tasks/waitForTask.ts} +3 -12
- package/src/tools/{androidDoctor.ts → workspace/androidDoctor.ts} +15 -10
- package/src/tools/{applyPatch.ts → workspace/applyPatch.ts} +38 -16
- package/src/tools/{discardWorktree.ts → workspace/discardWorktree.ts} +3 -3
- package/src/tools/workspace/index.ts +10 -0
- package/src/tools/workspace/listAgents.ts +75 -0
- package/src/tools/{listWorkspace.ts → workspace/listWorkspace.ts} +3 -3
- package/src/tools/{mergeWorktree.ts → workspace/mergeWorktree.ts} +3 -3
- package/src/tools/{readWorkspaceFile.ts → workspace/readWorkspaceFile.ts} +8 -8
- package/src/tools/{recommendAgentForTask.ts → workspace/recommendAgentForTask.ts} +3 -3
- package/src/tools/{searchWorkspace.ts → workspace/searchWorkspace.ts} +5 -5
- package/src/tools/workspace/syncFile.ts +292 -0
- package/src/utils/atomicFile.ts +74 -0
- package/src/utils/boundedFile.ts +116 -0
- package/src/utils/lockedJsonFile.ts +251 -0
- package/src/utils/platform.ts +31 -0
- package/src/utils/stableJson.ts +12 -0
- package/src/version.ts +3 -2
- package/src/watcherStatus.ts +19 -1
- package/ui/desktop-bootstrap.js +11 -0
- package/ui/desktop-bridge.js +36 -0
- package/ui/desktop.css +160 -0
- package/ui/getting-started.js +93 -0
- package/ui/i18n.js +484 -0
- package/ui/log-parser.js +30 -0
- package/ui/pages/audit.html +12 -0
- package/ui/pages/dashboard.html +94 -147
- package/ui/pages/direct-sessions.html +123 -26
- package/ui/pages/getting-started.html +43 -0
- package/ui/pages/logs.html +134 -22
- package/ui/pages/settings.html +101 -0
- package/ui/pages/task-detail.html +12 -0
- package/ui/pages/tasks.html +54 -8
- package/ui/pages/workspace.html +38 -1
- package/ui/settings.js +254 -0
- package/PatchWarden-Control.cmd +0 -6
- package/Restart-PatchWarden-Control.cmd +0 -6
- package/dist/test/unit/acceptance-engine.test.d.ts +0 -1
- package/dist/test/unit/acceptance-engine.test.js +0 -228
- package/dist/test/unit/agent-router.test.d.ts +0 -1
- package/dist/test/unit/agent-router.test.js +0 -287
- package/dist/test/unit/android-doctor.test.d.ts +0 -1
- package/dist/test/unit/android-doctor.test.js +0 -118
- package/dist/test/unit/apply-patch.test.d.ts +0 -1
- package/dist/test/unit/apply-patch.test.js +0 -225
- package/dist/test/unit/audit-checks.test.d.ts +0 -1
- package/dist/test/unit/audit-checks.test.js +0 -350
- package/dist/test/unit/chinese-path.test.d.ts +0 -1
- package/dist/test/unit/chinese-path.test.js +0 -91
- package/dist/test/unit/command-guard.test.d.ts +0 -1
- package/dist/test/unit/command-guard.test.js +0 -160
- package/dist/test/unit/create-task.test.d.ts +0 -1
- package/dist/test/unit/create-task.test.js +0 -197
- package/dist/test/unit/diagnose-task.test.d.ts +0 -1
- package/dist/test/unit/diagnose-task.test.js +0 -457
- package/dist/test/unit/direct-guards.test.d.ts +0 -1
- package/dist/test/unit/direct-guards.test.js +0 -328
- package/dist/test/unit/discovery-token-store.test.d.ts +0 -1
- package/dist/test/unit/discovery-token-store.test.js +0 -139
- package/dist/test/unit/evidence-pack.test.d.ts +0 -1
- package/dist/test/unit/evidence-pack.test.js +0 -224
- package/dist/test/unit/get-task-status.test.d.ts +0 -1
- package/dist/test/unit/get-task-status.test.js +0 -174
- package/dist/test/unit/get-task-summary.test.d.ts +0 -1
- package/dist/test/unit/get-task-summary.test.js +0 -146
- package/dist/test/unit/goal-graph.test.d.ts +0 -1
- package/dist/test/unit/goal-graph.test.js +0 -298
- package/dist/test/unit/goal-progress.test.d.ts +0 -1
- package/dist/test/unit/goal-progress.test.js +0 -381
- package/dist/test/unit/goal-report.test.d.ts +0 -1
- package/dist/test/unit/goal-report.test.js +0 -159
- package/dist/test/unit/goal-status.test.d.ts +0 -1
- package/dist/test/unit/goal-status.test.js +0 -215
- package/dist/test/unit/goal-store.test.d.ts +0 -1
- package/dist/test/unit/goal-store.test.js +0 -253
- package/dist/test/unit/goal-subgoal-task.test.d.ts +0 -1
- package/dist/test/unit/goal-subgoal-task.test.js +0 -55
- package/dist/test/unit/goal-tools-registry.test.d.ts +0 -1
- package/dist/test/unit/goal-tools-registry.test.js +0 -192
- package/dist/test/unit/handoff-export.test.d.ts +0 -1
- package/dist/test/unit/handoff-export.test.js +0 -263
- package/dist/test/unit/invoke-discovered-tool.test.d.ts +0 -1
- package/dist/test/unit/invoke-discovered-tool.test.js +0 -167
- package/dist/test/unit/logging.test.d.ts +0 -1
- package/dist/test/unit/logging.test.js +0 -397
- package/dist/test/unit/path-guard.test.d.ts +0 -1
- package/dist/test/unit/path-guard.test.js +0 -133
- package/dist/test/unit/post-task-cleanup.test.d.ts +0 -1
- package/dist/test/unit/post-task-cleanup.test.js +0 -48
- package/dist/test/unit/project-policy-release-mode.test.d.ts +0 -1
- package/dist/test/unit/project-policy-release-mode.test.js +0 -125
- package/dist/test/unit/reconcile-tasks.test.d.ts +0 -1
- package/dist/test/unit/reconcile-tasks.test.js +0 -456
- package/dist/test/unit/release-gate.test.d.ts +0 -1
- package/dist/test/unit/release-gate.test.js +0 -242
- package/dist/test/unit/run-task-loop.test.d.ts +0 -1
- package/dist/test/unit/run-task-loop.test.js +0 -380
- package/dist/test/unit/safe-status.test.d.ts +0 -1
- package/dist/test/unit/safe-status.test.js +0 -165
- package/dist/test/unit/safe-views.test.d.ts +0 -1
- package/dist/test/unit/safe-views.test.js +0 -171
- package/dist/test/unit/schema-drift-check.test.d.ts +0 -1
- package/dist/test/unit/schema-drift-check.test.js +0 -176
- package/dist/test/unit/sensitive-guard.test.d.ts +0 -1
- package/dist/test/unit/sensitive-guard.test.js +0 -104
- package/dist/test/unit/spec-kit-import.test.d.ts +0 -1
- package/dist/test/unit/spec-kit-import.test.js +0 -341
- package/dist/test/unit/subgoal-sync.test.d.ts +0 -1
- package/dist/test/unit/subgoal-sync.test.js +0 -183
- package/dist/test/unit/sync-file.test.d.ts +0 -1
- package/dist/test/unit/sync-file.test.js +0 -154
- package/dist/test/unit/tool-invocation-guard.test.d.ts +0 -1
- package/dist/test/unit/tool-invocation-guard.test.js +0 -432
- package/dist/test/unit/tool-usage-stats.test.d.ts +0 -1
- package/dist/test/unit/tool-usage-stats.test.js +0 -300
- package/dist/test/unit/toolSearch.test.d.ts +0 -1
- package/dist/test/unit/toolSearch.test.js +0 -571
- package/dist/test/unit/wait-for-task.test.d.ts +0 -1
- package/dist/test/unit/wait-for-task.test.js +0 -144
- package/dist/test/unit/watcher-status.test.d.ts +0 -1
- package/dist/test/unit/watcher-status.test.js +0 -169
- package/dist/test/unit/worktree-manager.test.d.ts +0 -1
- package/dist/test/unit/worktree-manager.test.js +0 -176
- package/dist/tools/applyPatch.js +0 -41
- package/dist/tools/cancelTask.d.ts +0 -30
- package/dist/tools/cancelTask.js +0 -64
- package/dist/tools/finalizeDirectSession.js +0 -84
- package/dist/tools/goalSubgoalTask.js +0 -110
- package/dist/tools/killTask.d.ts +0 -15
- package/dist/tools/listAgents.d.ts +0 -12
- package/dist/tools/listAgents.js +0 -42
- package/dist/tools/reconcileTasks.js +0 -352
- package/dist/tools/retryTask.js +0 -45
- package/dist/tools/runVerification.js +0 -36
- package/dist/tools/syncFile.js +0 -65
- package/docs/control-center/control-center-daily-driver.md +0 -211
- package/docs/control-center/control-center-mvp.md +0 -205
- package/docs/control-center/control-center-phase2.md +0 -159
- package/docs/execution-plan-2026-07-09.md +0 -315
- package/docs/release-v0.6.0.md +0 -71
- package/docs/release-v0.6.1.md +0 -75
- package/docs/release-v0.6.4.md +0 -45
- package/docs/roadmap-execution-and-acceptance.md +0 -365
- package/src/test/unit/acceptance-engine.test.ts +0 -261
- package/src/test/unit/agent-router.test.ts +0 -342
- package/src/test/unit/android-doctor.test.ts +0 -158
- package/src/test/unit/apply-patch.test.ts +0 -293
- package/src/test/unit/audit-checks.test.ts +0 -567
- package/src/test/unit/chinese-path.test.ts +0 -106
- package/src/test/unit/command-guard.test.ts +0 -221
- package/src/test/unit/create-task.test.ts +0 -255
- package/src/test/unit/diagnose-task.test.ts +0 -544
- package/src/test/unit/direct-guards.test.ts +0 -467
- package/src/test/unit/discovery-token-store.test.ts +0 -181
- package/src/test/unit/evidence-pack.test.ts +0 -251
- package/src/test/unit/get-task-status.test.ts +0 -203
- package/src/test/unit/get-task-summary.test.ts +0 -173
- package/src/test/unit/goal-graph.test.ts +0 -347
- package/src/test/unit/goal-progress.test.ts +0 -538
- package/src/test/unit/goal-report.test.ts +0 -189
- package/src/test/unit/goal-status.test.ts +0 -270
- package/src/test/unit/goal-store.test.ts +0 -318
- package/src/test/unit/goal-subgoal-task.test.ts +0 -72
- package/src/test/unit/goal-tools-registry.test.ts +0 -245
- package/src/test/unit/handoff-export.test.ts +0 -295
- package/src/test/unit/invoke-discovered-tool.test.ts +0 -216
- package/src/test/unit/logging.test.ts +0 -497
- package/src/test/unit/path-guard.test.ts +0 -180
- package/src/test/unit/post-task-cleanup.test.ts +0 -53
- package/src/test/unit/project-policy-release-mode.test.ts +0 -156
- package/src/test/unit/reconcile-tasks.test.ts +0 -551
- package/src/test/unit/release-gate.test.ts +0 -314
- package/src/test/unit/run-task-loop.test.ts +0 -425
- package/src/test/unit/safe-status.test.ts +0 -187
- package/src/test/unit/safe-views.test.ts +0 -184
- package/src/test/unit/schema-drift-check.test.ts +0 -260
- package/src/test/unit/sensitive-guard.test.ts +0 -124
- package/src/test/unit/spec-kit-import.test.ts +0 -429
- package/src/test/unit/subgoal-sync.test.ts +0 -236
- package/src/test/unit/sync-file.test.ts +0 -231
- package/src/test/unit/tool-invocation-guard.test.ts +0 -542
- package/src/test/unit/tool-usage-stats.test.ts +0 -384
- package/src/test/unit/toolSearch.test.ts +0 -756
- package/src/test/unit/wait-for-task.test.ts +0 -176
- package/src/test/unit/watcher-status.test.ts +0 -190
- package/src/test/unit/worktree-manager.test.ts +0 -247
- package/src/tools/cancelTask.ts +0 -70
- package/src/tools/listAgents.ts +0 -54
- package/src/tools/retryTask.ts +0 -53
- package/src/tools/syncFile.ts +0 -122
- /package/dist/tools/{toolCatalog.d.ts → catalog/toolCatalog.d.ts} +0 -0
- /package/dist/tools/{toolSearch.d.ts → catalog/toolSearch.d.ts} +0 -0
- /package/dist/tools/{toolSearch.js → catalog/toolSearch.js} +0 -0
- /package/dist/tools/{toolUsageStats.d.ts → catalog/toolUsageStats.d.ts} +0 -0
- /package/dist/tools/{toolUsageStats.js → catalog/toolUsageStats.js} +0 -0
- /package/dist/tools/{createDirectSession.d.ts → direct/createDirectSession.d.ts} +0 -0
- /package/dist/tools/{runDirectVerificationBundle.d.ts → direct/runDirectVerificationBundle.d.ts} +0 -0
- /package/dist/tools/{getPlan.d.ts → goals/getPlan.d.ts} +0 -0
- /package/dist/tools/{savePlan.d.ts → goals/savePlan.d.ts} +0 -0
- /package/dist/tools/{evidencePack.d.ts → tasks/evidencePack.d.ts} +0 -0
- /package/dist/tools/{getTaskFile.d.ts → tasks/getTaskFile.d.ts} +0 -0
- /package/dist/tools/{getTaskProgress.d.ts → tasks/getTaskProgress.d.ts} +0 -0
- /package/dist/tools/{getTaskProgress.js → tasks/getTaskProgress.js} +0 -0
- /package/dist/tools/{getTaskStdoutTail.d.ts → tasks/getTaskStdoutTail.d.ts} +0 -0
- /package/dist/tools/{getTaskSummary.d.ts → tasks/getTaskSummary.d.ts} +0 -0
- /package/dist/tools/{killTask.js → tasks/killTask.js} +0 -0
- /package/dist/tools/{retryTask.d.ts → tasks/retryTask.d.ts} +0 -0
- /package/dist/tools/{runVerification.d.ts → tasks/runVerification.d.ts} +0 -0
- /package/dist/tools/{taskLineage.d.ts → tasks/taskLineage.d.ts} +0 -0
- /package/dist/tools/{waitForTask.d.ts → tasks/waitForTask.d.ts} +0 -0
- /package/dist/tools/{androidDoctor.d.ts → workspace/androidDoctor.d.ts} +0 -0
- /package/dist/tools/{discardWorktree.d.ts → workspace/discardWorktree.d.ts} +0 -0
- /package/dist/tools/{listWorkspace.d.ts → workspace/listWorkspace.d.ts} +0 -0
- /package/dist/tools/{mergeWorktree.d.ts → workspace/mergeWorktree.d.ts} +0 -0
- /package/dist/tools/{readWorkspaceFile.d.ts → workspace/readWorkspaceFile.d.ts} +0 -0
- /package/dist/tools/{recommendAgentForTask.d.ts → workspace/recommendAgentForTask.d.ts} +0 -0
- /package/dist/tools/{searchWorkspace.d.ts → workspace/searchWorkspace.d.ts} +0 -0
- /package/src/tools/{toolSearch.ts → catalog/toolSearch.ts} +0 -0
- /package/src/tools/{toolUsageStats.ts → catalog/toolUsageStats.ts} +0 -0
- /package/src/tools/{getTaskProgress.ts → tasks/getTaskProgress.ts} +0 -0
- /package/src/tools/{killTask.ts → tasks/killTask.ts} +0 -0
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
2
|
-
import { getConfig } from "
|
|
3
|
-
import { createWorktree } from "
|
|
4
|
-
import { guardWorkspacePath } from "
|
|
5
|
-
import { createDirectSession } from "
|
|
2
|
+
import { getConfig } from "../../config.js";
|
|
3
|
+
import { createWorktree } from "../../goal/worktreeManager.js";
|
|
4
|
+
import { guardWorkspacePath } from "../../security/pathGuard.js";
|
|
5
|
+
import { createDirectSession } from "../direct/createDirectSession.js";
|
|
6
6
|
import { createTask, type CreateTaskInput } from "./createTask.js";
|
|
7
|
-
import { recommendAgentForTask } from "
|
|
8
|
-
import { runDirectVerificationBundle } from "
|
|
7
|
+
import { recommendAgentForTask } from "../workspace/recommendAgentForTask.js";
|
|
8
|
+
import { runDirectVerificationBundle } from "../direct/runDirectVerificationBundle.js";
|
|
9
9
|
import { waitForTask } from "./waitForTask.js";
|
|
10
|
-
import { safeAudit, safeAuditDirectSession, safeFinalizeDirectSession, safeResult, safeTestSummary } from "
|
|
11
|
-
import type { TaskTemplateName } from "
|
|
10
|
+
import { safeAudit, safeAuditDirectSession, safeFinalizeDirectSession, safeResult, safeTestSummary } from "../diagnostics/safeViews.js";
|
|
11
|
+
import type { TaskTemplateName } from "../taskTemplates.js";
|
|
12
|
+
import { isTerminalTaskStatus } from "./taskStates.js";
|
|
12
13
|
import {
|
|
13
14
|
createLineageId,
|
|
14
15
|
writeTaskLineage,
|
|
@@ -86,16 +87,6 @@ const DEFAULT_DEPS: RunTaskLoopDeps = {
|
|
|
86
87
|
sleep,
|
|
87
88
|
};
|
|
88
89
|
|
|
89
|
-
const TERMINAL_STATUSES = new Set([
|
|
90
|
-
"done",
|
|
91
|
-
"done_by_agent",
|
|
92
|
-
"failed",
|
|
93
|
-
"failed_verification",
|
|
94
|
-
"failed_scope_violation",
|
|
95
|
-
"failed_policy_violation",
|
|
96
|
-
"canceled",
|
|
97
|
-
]);
|
|
98
|
-
|
|
99
90
|
export async function runTaskLoop(input: RunTaskLoopInput): Promise<RunTaskLoopOutput> {
|
|
100
91
|
return runTaskLoopWithDeps(input, DEFAULT_DEPS);
|
|
101
92
|
}
|
|
@@ -202,18 +193,23 @@ export async function runTaskLoopWithDeps(
|
|
|
202
193
|
timeout_seconds: normalized.task_timeout_seconds,
|
|
203
194
|
execution_mode: "assess_only",
|
|
204
195
|
};
|
|
205
|
-
const assessment = await deps.createTask(assessmentInput
|
|
196
|
+
const assessment = asRecord(await deps.createTask(assessmentInput));
|
|
206
197
|
if (assessment.decision === "blocked") {
|
|
207
|
-
return finalize(
|
|
198
|
+
return finalize(
|
|
199
|
+
"blocked",
|
|
200
|
+
"high_risk_blocked",
|
|
201
|
+
"Risk assessment blocked task execution.",
|
|
202
|
+
asArray(assessment.reason_codes).map(String).join(", "),
|
|
203
|
+
);
|
|
208
204
|
}
|
|
209
205
|
if (assessment.decision === "needs_confirm") {
|
|
210
206
|
return finalize("blocked", "user_confirmation_required", "Ask the user to confirm the assessment before executing the loop.");
|
|
211
207
|
}
|
|
212
208
|
|
|
213
|
-
const created = await deps.createTask({
|
|
209
|
+
const created = asRecord(await deps.createTask({
|
|
214
210
|
execution_mode: "execute",
|
|
215
211
|
assessment_id: String(assessment.assessment_id || ""),
|
|
216
|
-
})
|
|
212
|
+
}));
|
|
217
213
|
const taskId = String(created.task_id || "");
|
|
218
214
|
if (!taskId) {
|
|
219
215
|
return finalize("failed", "policy_blocked", "create_task returned no task_id.", "create_task returned no task_id");
|
|
@@ -226,9 +222,9 @@ export async function runTaskLoopWithDeps(
|
|
|
226
222
|
return finalize("blocked", wait.stop_reason, wait.next_action, wait.error);
|
|
227
223
|
}
|
|
228
224
|
|
|
229
|
-
const result = deps.safeResult(taskId, { max_items: 8 })
|
|
230
|
-
const tests = deps.safeTestSummary(taskId)
|
|
231
|
-
const audit = deps.safeAudit(taskId, { max_items: 8 })
|
|
225
|
+
const result = asRecord(deps.safeResult(taskId, { max_items: 8 }));
|
|
226
|
+
const tests = asRecord(deps.safeTestSummary(taskId));
|
|
227
|
+
const audit = asRecord(deps.safeAudit(taskId, { max_items: 8 }));
|
|
232
228
|
const round = buildRound(iteration, taskId, role, result, tests, audit);
|
|
233
229
|
lineage.rounds.push(round);
|
|
234
230
|
lineage.updated_at = deps.now().toISOString();
|
|
@@ -267,8 +263,8 @@ async function waitUntilTerminal(
|
|
|
267
263
|
): Promise<{ stop_reason?: TaskLoopStopReason; next_action: string; error?: string }> {
|
|
268
264
|
const deadline = Date.now() + timeoutSeconds * 1000;
|
|
269
265
|
while (Date.now() < deadline) {
|
|
270
|
-
const waited = await deps.waitForTask(taskId, 30)
|
|
271
|
-
if (waited.terminal ||
|
|
266
|
+
const waited = await deps.waitForTask(taskId, 30);
|
|
267
|
+
if (waited.terminal || isTerminalTaskStatus(String(waited.status))) {
|
|
272
268
|
return { next_action: waited.next_action || "safe_audit" };
|
|
273
269
|
}
|
|
274
270
|
if (waited.next_tool_call?.name === "health_check" || waited.continuation_required === false) {
|
|
@@ -291,23 +287,35 @@ function buildRound(
|
|
|
291
287
|
iteration: number,
|
|
292
288
|
taskId: string,
|
|
293
289
|
role: TaskLineageRound["role"],
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
290
|
+
resultValue: unknown,
|
|
291
|
+
testsValue: unknown,
|
|
292
|
+
auditValue: unknown,
|
|
297
293
|
): TaskLineageRound {
|
|
298
|
-
const
|
|
299
|
-
const
|
|
294
|
+
const result = asRecord(resultValue);
|
|
295
|
+
const tests = asRecord(testsValue);
|
|
296
|
+
const audit = asRecord(auditValue);
|
|
297
|
+
const resultVerification = asRecord(result.verification);
|
|
298
|
+
const auditAcceptance = asRecord(audit.acceptance);
|
|
299
|
+
const failChecks = asArray(audit.fail_checks).map((entry) => {
|
|
300
|
+
const check = asRecord(entry);
|
|
301
|
+
return String(check.name || entry);
|
|
302
|
+
});
|
|
303
|
+
const warnChecks = asArray(audit.warn_checks).map((entry) => {
|
|
304
|
+
const check = asRecord(entry);
|
|
305
|
+
return String(check.name || entry);
|
|
306
|
+
});
|
|
307
|
+
const recommendedActions = asArray(audit.recommended_next_actions);
|
|
300
308
|
return {
|
|
301
309
|
iteration,
|
|
302
310
|
task_id: taskId,
|
|
303
311
|
role,
|
|
304
312
|
status: String(result.status || "unknown"),
|
|
305
313
|
terminal: Boolean(result.terminal),
|
|
306
|
-
verification_status: String(tests.status ||
|
|
307
|
-
audit_verdict: String(audit.verdict ||
|
|
314
|
+
verification_status: String(tests.status || resultVerification.status || "not_available"),
|
|
315
|
+
audit_verdict: String(audit.verdict || auditAcceptance.verdict || "unknown"),
|
|
308
316
|
fail_checks: failChecks,
|
|
309
317
|
warn_checks: warnChecks,
|
|
310
|
-
next_action: String(result.next_action ||
|
|
318
|
+
next_action: String(result.next_action || recommendedActions[0] || "review_task"),
|
|
311
319
|
};
|
|
312
320
|
}
|
|
313
321
|
|
|
@@ -322,21 +330,26 @@ function isSuccessfulRound(round: TaskLineageRound): boolean {
|
|
|
322
330
|
);
|
|
323
331
|
}
|
|
324
332
|
|
|
325
|
-
function isHardStop(round: TaskLineageRound,
|
|
326
|
-
if (["failed_scope_violation", "failed_policy_violation", "canceled"].includes(round.status)) return true;
|
|
333
|
+
function isHardStop(round: TaskLineageRound, resultValue: unknown, auditValue: unknown, stopOnHighRisk: boolean): boolean {
|
|
334
|
+
if (["failed_scope_violation", "failed_policy_violation", "canceled", "timeout"].includes(round.status)) return true;
|
|
327
335
|
if (!stopOnHighRisk) return false;
|
|
328
336
|
const checkNames = [...round.fail_checks, ...round.warn_checks].join(" ").toLowerCase();
|
|
329
|
-
const
|
|
337
|
+
const result = asRecord(resultValue);
|
|
338
|
+
const audit = asRecord(auditValue);
|
|
339
|
+
const reason = String(result.failure_reason || asRecord(audit.acceptance).reason || "").toLowerCase();
|
|
330
340
|
return /scope|secret|sensitive|publish|release|policy|push/.test(`${checkNames} ${reason}`);
|
|
331
341
|
}
|
|
332
342
|
|
|
333
|
-
function hardStopReason(round: TaskLineageRound,
|
|
343
|
+
function hardStopReason(round: TaskLineageRound, resultValue: unknown): TaskLoopStopReason {
|
|
344
|
+
const result = asRecord(resultValue);
|
|
334
345
|
if (round.status === "failed_scope_violation" || round.status === "failed_policy_violation") return "policy_blocked";
|
|
346
|
+
if (round.status === "timeout") return "agent_timeout";
|
|
335
347
|
if (String(result.failure_reason || "").toLowerCase().includes("timeout")) return "agent_timeout";
|
|
336
348
|
return "high_risk_blocked";
|
|
337
349
|
}
|
|
338
350
|
|
|
339
|
-
function buildFixGoal(originalGoal: string,
|
|
351
|
+
function buildFixGoal(originalGoal: string, resultValue: unknown, round: TaskLineageRound): string {
|
|
352
|
+
const result = asRecord(resultValue);
|
|
340
353
|
const failedCommand = result.failed_command ? ` Failed command: ${result.failed_command}.` : "";
|
|
341
354
|
return [
|
|
342
355
|
`Fix the failing verification for this PatchWarden loop without changing unrelated behavior.`,
|
|
@@ -413,8 +426,12 @@ async function runDirectVerification(
|
|
|
413
426
|
commands: normalized.direct_verify_commands,
|
|
414
427
|
timeout_seconds: normalized.direct_verify_timeout_seconds,
|
|
415
428
|
});
|
|
416
|
-
const finalized = await deps.safeFinalizeDirectSession(sessionId, { max_items: 8 })
|
|
417
|
-
const audit = deps.safeAuditDirectSession(sessionId, { max_items: 8 })
|
|
429
|
+
const finalized = asRecord(await deps.safeFinalizeDirectSession(sessionId, { max_items: 8 }));
|
|
430
|
+
const audit = asRecord(deps.safeAuditDirectSession(sessionId, { max_items: 8 }));
|
|
431
|
+
const auditEvidence = asRecord(audit.evidence);
|
|
432
|
+
const auditDecision = audit.decision === "pass" || audit.decision === "warn" || audit.decision === "fail"
|
|
433
|
+
? audit.decision
|
|
434
|
+
: "not_run";
|
|
418
435
|
const evidence: TaskLineageDirectSession = {
|
|
419
436
|
session_id: sessionId,
|
|
420
437
|
status: bundle.status,
|
|
@@ -422,8 +439,8 @@ async function runDirectVerification(
|
|
|
422
439
|
passed_commands: bundle.passed_commands,
|
|
423
440
|
failed_commands: bundle.failed_commands,
|
|
424
441
|
timed_out_commands: bundle.timed_out_commands,
|
|
425
|
-
audit_decision:
|
|
426
|
-
changed_files_total: Number(finalized.changed_files_total ||
|
|
442
|
+
audit_decision: auditDecision,
|
|
443
|
+
changed_files_total: Number(finalized.changed_files_total || auditEvidence.changed_files_total || 0),
|
|
427
444
|
next_action: String(audit.next_action || bundle.next_action || "review_direct_session"),
|
|
428
445
|
};
|
|
429
446
|
if (bundle.status !== "passed") {
|
|
@@ -435,7 +452,7 @@ async function runDirectVerification(
|
|
|
435
452
|
error: "Direct verification failed.",
|
|
436
453
|
};
|
|
437
454
|
}
|
|
438
|
-
if (
|
|
455
|
+
if (auditDecision === "fail") {
|
|
439
456
|
return {
|
|
440
457
|
evidence,
|
|
441
458
|
stop_reason: "direct_audit_failed",
|
|
@@ -448,7 +465,7 @@ async function runDirectVerification(
|
|
|
448
465
|
evidence,
|
|
449
466
|
final_status: "accepted",
|
|
450
467
|
next_action: "accept",
|
|
451
|
-
warning:
|
|
468
|
+
warning: auditDecision === "warn" ? "Direct audit completed with warnings." : undefined,
|
|
452
469
|
};
|
|
453
470
|
} catch (err) {
|
|
454
471
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -467,6 +484,16 @@ async function runDirectVerification(
|
|
|
467
484
|
}
|
|
468
485
|
}
|
|
469
486
|
|
|
487
|
+
function asRecord(value: unknown): Record<string, unknown> {
|
|
488
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
489
|
+
? value as Record<string, unknown>
|
|
490
|
+
: {};
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function asArray(value: unknown): unknown[] {
|
|
494
|
+
return Array.isArray(value) ? value : [];
|
|
495
|
+
}
|
|
496
|
+
|
|
470
497
|
function normalizeInput(input: RunTaskLoopInput): Required<Omit<RunTaskLoopInput, "agent" | "scope_files" | "worktree_base_branch">> & { agent?: string; scope_files?: string[]; worktree_base_branch?: string } {
|
|
471
498
|
const config = getConfig();
|
|
472
499
|
const repoPath = String(input.repo_path || "").trim();
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
readDirectSession,
|
|
3
3
|
appendDirectSessionVerificationRun,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import {
|
|
4
|
+
withDirectSessionMutationLockAsync,
|
|
5
|
+
} from "../../direct/directSessionStore.js";
|
|
6
|
+
import { guardDirectSessionActive } from "../../direct/directGuards.js";
|
|
7
|
+
import { runDirectVerification } from "../../direct/directVerification.js";
|
|
7
8
|
|
|
8
9
|
export interface RunVerificationInput {
|
|
9
10
|
session_id: string;
|
|
@@ -29,6 +30,7 @@ export interface RunVerificationOutput {
|
|
|
29
30
|
export async function runVerification(
|
|
30
31
|
input: RunVerificationInput
|
|
31
32
|
): Promise<RunVerificationOutput> {
|
|
33
|
+
return withDirectSessionMutationLockAsync(input.session_id, async () => {
|
|
32
34
|
// 1. Read session and guard active
|
|
33
35
|
const session = readDirectSession(input.session_id);
|
|
34
36
|
guardDirectSessionActive(session);
|
|
@@ -63,4 +65,5 @@ export async function runVerification(
|
|
|
63
65
|
? "Call finalize_direct_session to complete the session."
|
|
64
66
|
: "Review the verification output and apply_patch to fix issues.",
|
|
65
67
|
};
|
|
68
|
+
});
|
|
66
69
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
|
-
import { getConfig } from "
|
|
5
|
-
import { guardReadPath } from "
|
|
6
|
-
import { redactSensitiveValue } from "
|
|
7
|
-
import { PatchWardenError } from "
|
|
4
|
+
import { getConfig } from "../../config.js";
|
|
5
|
+
import { guardReadPath } from "../../security/pathGuard.js";
|
|
6
|
+
import { redactSensitiveValue } from "../../security/contentRedaction.js";
|
|
7
|
+
import { PatchWardenError } from "../../errors.js";
|
|
8
|
+
import { atomicWriteFileSync, atomicWriteJsonFileSync } from "../../utils/atomicFile.js";
|
|
8
9
|
|
|
9
10
|
export type TaskLoopStopReason =
|
|
10
11
|
| "success"
|
|
@@ -119,8 +120,8 @@ export function writeTaskLineage(record: TaskLineageRecord): SafeTaskLineage {
|
|
|
119
120
|
const lineageDir = resolve(config.workspaceRoot, ".patchwarden", "lineages", record.lineage_id);
|
|
120
121
|
mkdirSync(lineageDir, { recursive: true });
|
|
121
122
|
const safeRecord = redactSensitiveValue(record).value as TaskLineageRecord;
|
|
122
|
-
|
|
123
|
-
|
|
123
|
+
atomicWriteJsonFileSync(join(lineageDir, "lineage.json"), safeRecord);
|
|
124
|
+
atomicWriteFileSync(join(lineageDir, "SUMMARY.md"), buildSummaryMarkdown(safeRecord));
|
|
124
125
|
return toSafeTaskLineage(safeRecord);
|
|
125
126
|
}
|
|
126
127
|
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { getTaskFile, GetTaskFileOutput } from "./getTaskFile.js";
|
|
2
|
-
import { existsSync,
|
|
2
|
+
import { existsSync, statSync } from "node:fs";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
|
-
import { getConfig, getTasksDir } from "
|
|
5
|
-
import { guardPath } from "
|
|
6
|
-
import { redactSensitiveContent } from "
|
|
4
|
+
import { getConfig, getTasksDir } from "../../config.js";
|
|
5
|
+
import { guardPath } from "../../security/pathGuard.js";
|
|
6
|
+
import { redactSensitiveContent } from "../../security/contentRedaction.js";
|
|
7
|
+
import {
|
|
8
|
+
readTextFilePrefixSync,
|
|
9
|
+
readTextFileTailLinesSync,
|
|
10
|
+
} from "../../utils/boundedFile.js";
|
|
7
11
|
|
|
8
12
|
export function getResult(taskId: string): GetTaskFileOutput {
|
|
9
13
|
return getTaskFile(taskId, "result.md");
|
|
@@ -23,26 +27,25 @@ export function getDiff(taskId: string): GetTaskFileOutput {
|
|
|
23
27
|
}
|
|
24
28
|
const filePath = existsSync(preferred) ? preferred : fallback;
|
|
25
29
|
guardPath(filePath, config.workspaceRoot, config.tasksDir);
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const patchHead =
|
|
29
|
-
const rawReturnedBytes = Buffer.byteLength(patchHead, "utf-8");
|
|
30
|
+
const bounded = readTextFilePrefixSync(filePath, config.maxReadFileBytes);
|
|
31
|
+
const size = bounded.totalBytes;
|
|
32
|
+
const patchHead = bounded.content;
|
|
30
33
|
const redaction = redactSensitiveContent(patchHead);
|
|
31
34
|
const content = redaction.content;
|
|
32
|
-
const diff = {
|
|
35
|
+
const diff: GetTaskFileOutput & { truncated: boolean } = {
|
|
33
36
|
task_id: taskId,
|
|
34
37
|
filename: existsSync(preferred) ? "diff.patch" : "git.diff",
|
|
35
38
|
content,
|
|
36
39
|
path: filePath,
|
|
37
40
|
available: true,
|
|
38
|
-
truncated:
|
|
41
|
+
truncated: bounded.truncated,
|
|
39
42
|
total_bytes: size,
|
|
40
43
|
returned_bytes: Buffer.byteLength(content, "utf-8"),
|
|
41
44
|
patch_head: content,
|
|
42
45
|
diff_patch_path: filePath,
|
|
43
46
|
redacted: redaction.redacted,
|
|
44
47
|
redaction_categories: redaction.redaction_categories,
|
|
45
|
-
} as GetTaskFileOutput;
|
|
48
|
+
} as GetTaskFileOutput & { truncated: boolean };
|
|
46
49
|
try {
|
|
47
50
|
const evidence = JSON.parse(getTaskFile(taskId, "changed-files.json").content);
|
|
48
51
|
return {
|
|
@@ -51,7 +54,7 @@ export function getDiff(taskId: string): GetTaskFileOutput {
|
|
|
51
54
|
diff_available: Boolean(evidence.diff_available),
|
|
52
55
|
workspace_dirty_before: Boolean(evidence.workspace_dirty_before),
|
|
53
56
|
workspace_dirty_after: Boolean(evidence.workspace_dirty_after),
|
|
54
|
-
diff_truncated: Boolean(evidence.diff_truncated ||
|
|
57
|
+
diff_truncated: Boolean(evidence.diff_truncated || diff.truncated),
|
|
55
58
|
diff_size_bytes: Number(evidence.diff_size_bytes || size),
|
|
56
59
|
additions: Number(evidence.additions || 0),
|
|
57
60
|
deletions: Number(evidence.deletions || 0),
|
|
@@ -101,8 +104,7 @@ export function getTaskLogTail(
|
|
|
101
104
|
|
|
102
105
|
const stat = statSync(filePath);
|
|
103
106
|
const totalBytes = stat.size;
|
|
104
|
-
const
|
|
105
|
-
const tail = raw.split("\n").slice(-maxLines).join("\n");
|
|
107
|
+
const tail = readTextFileTailLinesSync(filePath, maxLines);
|
|
106
108
|
const content = applyRedact ? redactSensitiveContent(tail) : { content: tail, redacted: false, redaction_categories: [] as string[] };
|
|
107
109
|
|
|
108
110
|
return {
|
|
@@ -134,13 +136,18 @@ function getTaskFileTail(
|
|
|
134
136
|
}
|
|
135
137
|
|
|
136
138
|
const stat = statSync(filePath);
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
+
const maxBytes = options.maxBytes > 0
|
|
140
|
+
? Math.min(options.maxBytes, config.maxReadFileBytes)
|
|
141
|
+
: config.maxReadFileBytes;
|
|
142
|
+
let content: string;
|
|
139
143
|
if (options.tailLines > 0) {
|
|
140
|
-
content =
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
+
content = readTextFileTailLinesSync(
|
|
145
|
+
filePath,
|
|
146
|
+
Math.min(Math.max(1, options.tailLines), 1000),
|
|
147
|
+
maxBytes,
|
|
148
|
+
);
|
|
149
|
+
} else {
|
|
150
|
+
content = readTextFilePrefixSync(filePath, maxBytes).content;
|
|
144
151
|
}
|
|
145
152
|
const redaction = redactSensitiveContent(content);
|
|
146
153
|
return {
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export const ACTIVE_TASK_STATUSES = new Set([
|
|
2
|
+
"pending",
|
|
3
|
+
"running",
|
|
4
|
+
"executing_agent",
|
|
5
|
+
"verifying",
|
|
6
|
+
"collecting_artifacts",
|
|
7
|
+
"cancel_requested",
|
|
8
|
+
]);
|
|
9
|
+
|
|
10
|
+
export const TERMINAL_TASK_STATUSES = new Set([
|
|
11
|
+
"done",
|
|
12
|
+
"done_by_agent",
|
|
13
|
+
"accepted",
|
|
14
|
+
"rejected",
|
|
15
|
+
"needs_fix",
|
|
16
|
+
"blocked",
|
|
17
|
+
"failed",
|
|
18
|
+
"failed_verification",
|
|
19
|
+
"failed_scope_violation",
|
|
20
|
+
"failed_policy_violation",
|
|
21
|
+
"failed_stale",
|
|
22
|
+
"orphaned",
|
|
23
|
+
"timeout",
|
|
24
|
+
"canceled",
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
export function isTerminalTaskStatus(status: string): boolean {
|
|
28
|
+
return TERMINAL_TASK_STATUSES.has(status);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function isActiveTaskStatus(status: string): boolean {
|
|
32
|
+
return ACTIVE_TASK_STATUSES.has(status);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Terminal states are immutable; active states may only advance or terminate. */
|
|
36
|
+
export function isAllowedTaskStatusTransition(from: string, to: string): boolean {
|
|
37
|
+
if (from === to) return true;
|
|
38
|
+
if (isTerminalTaskStatus(from)) return false;
|
|
39
|
+
if (!isActiveTaskStatus(from) || !isActiveTaskStatus(to) && !isTerminalTaskStatus(to)) return false;
|
|
40
|
+
|
|
41
|
+
if (from === "pending") {
|
|
42
|
+
return to === "running" || isTerminalTaskStatus(to);
|
|
43
|
+
}
|
|
44
|
+
if (to === "pending") return false;
|
|
45
|
+
|
|
46
|
+
const forwardActive: Record<string, Set<string>> = {
|
|
47
|
+
running: new Set(["executing_agent", "verifying", "collecting_artifacts", "cancel_requested"]),
|
|
48
|
+
executing_agent: new Set(["verifying", "collecting_artifacts", "cancel_requested"]),
|
|
49
|
+
verifying: new Set(["collecting_artifacts", "cancel_requested"]),
|
|
50
|
+
collecting_artifacts: new Set(["cancel_requested"]),
|
|
51
|
+
cancel_requested: new Set(),
|
|
52
|
+
};
|
|
53
|
+
return isTerminalTaskStatus(to) || Boolean(forwardActive[from]?.has(to));
|
|
54
|
+
}
|
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
2
2
|
import { getTaskStatus } from "./getTaskStatus.js";
|
|
3
3
|
import { getTaskSummary, type TaskSummaryResult } from "./getTaskSummary.js";
|
|
4
|
-
|
|
5
|
-
const TERMINAL_STATUSES = new Set([
|
|
6
|
-
"done",
|
|
7
|
-
"done_by_agent",
|
|
8
|
-
"failed",
|
|
9
|
-
"failed_verification",
|
|
10
|
-
"failed_scope_violation",
|
|
11
|
-
"failed_policy_violation",
|
|
12
|
-
"canceled",
|
|
13
|
-
]);
|
|
4
|
+
import { isTerminalTaskStatus } from "./taskStates.js";
|
|
14
5
|
|
|
15
6
|
export interface WaitForTaskProgressSummary {
|
|
16
7
|
phase: string;
|
|
@@ -45,12 +36,12 @@ export async function waitForTask(taskId: string, waitSeconds = 25): Promise<Wai
|
|
|
45
36
|
const deadline = started + waitSeconds * 1000;
|
|
46
37
|
let status = getTaskStatus(taskId);
|
|
47
38
|
|
|
48
|
-
while (!
|
|
39
|
+
while (!isTerminalTaskStatus(status.status) && Date.now() < deadline) {
|
|
49
40
|
await sleep(Math.min(500, Math.max(1, deadline - Date.now())));
|
|
50
41
|
status = getTaskStatus(taskId);
|
|
51
42
|
}
|
|
52
43
|
|
|
53
|
-
const terminal =
|
|
44
|
+
const terminal = isTerminalTaskStatus(status.status);
|
|
54
45
|
const executionBlocked = !terminal && status.execution_blocked;
|
|
55
46
|
const elapsed = Math.round((Date.now() - started) / 1000);
|
|
56
47
|
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* and inspects files/directories/env vars that already exist.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { execSync } from "node:child_process";
|
|
12
11
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
13
12
|
import { join } from "node:path";
|
|
13
|
+
import { runSimpleProcessSync } from "../../runner/simpleProcess.js";
|
|
14
14
|
|
|
15
15
|
// ── Types ──────────────────────────────────────────────────────────
|
|
16
16
|
|
|
@@ -47,17 +47,22 @@ const SDK_MISSING_REASON =
|
|
|
47
47
|
// ── Helpers ────────────────────────────────────────────────────────
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
|
-
* Run a shell command and return its trimmed combined stdout/stderr output.
|
|
50
|
+
* Run a shell-free command and return its trimmed combined stdout/stderr output.
|
|
51
51
|
* Returns an empty string when the command is missing or fails so callers can
|
|
52
52
|
* treat absence gracefully. A timeout guards against hanging processes.
|
|
53
53
|
*/
|
|
54
|
-
function runCmd(
|
|
54
|
+
function runCmd(command: string, args: readonly string[], cwd = process.cwd()): string {
|
|
55
55
|
try {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
const result = runSimpleProcessSync({
|
|
57
|
+
command,
|
|
58
|
+
args: [...args],
|
|
59
|
+
cwd,
|
|
60
|
+
timeoutMs: 8000,
|
|
61
|
+
maxStdoutBytes: 64 * 1024,
|
|
62
|
+
maxStderrBytes: 64 * 1024,
|
|
63
|
+
});
|
|
64
|
+
if (result.exitCode !== 0) return "";
|
|
65
|
+
return `${result.stdout}\n${result.stderr}`.trim();
|
|
61
66
|
} catch {
|
|
62
67
|
return "";
|
|
63
68
|
}
|
|
@@ -128,8 +133,8 @@ export function diagnoseAndroidBuild(repoPath: string): AndroidBuildDiagnostic {
|
|
|
128
133
|
const sdkExists = sdkRoot !== "" && isDirectory(sdkRoot);
|
|
129
134
|
|
|
130
135
|
// 1. java -version ------------------------------------------------------
|
|
131
|
-
// `java -version` writes to stderr;
|
|
132
|
-
const javaOutput = runCmd("java -version
|
|
136
|
+
// `java -version` normally writes to stderr; runSimpleProcessSync captures both.
|
|
137
|
+
const javaOutput = runCmd("java", ["-version"], repoPath);
|
|
133
138
|
const javaVersion = parseJavaVersion(javaOutput);
|
|
134
139
|
if (javaVersion) {
|
|
135
140
|
const major = parseJavaMajor(javaVersion);
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
import { getConfig } from "
|
|
1
|
+
import { getConfig } from "../../config.js";
|
|
2
|
+
import { PatchWardenError } from "../../errors.js";
|
|
2
3
|
import {
|
|
3
4
|
readDirectSession,
|
|
4
5
|
appendDirectSessionOperation,
|
|
5
|
-
|
|
6
|
+
withDirectSessionMutationLock,
|
|
7
|
+
} from "../../direct/directSessionStore.js";
|
|
6
8
|
import {
|
|
7
9
|
guardDirectSessionActive,
|
|
8
10
|
guardDirectWritePath,
|
|
9
11
|
guardDirectPatchSize,
|
|
10
|
-
} from "
|
|
12
|
+
} from "../../direct/directGuards.js";
|
|
11
13
|
import {
|
|
12
14
|
applyPatchOperations,
|
|
13
|
-
validateExpectedSha256,
|
|
14
15
|
computeFileSha256,
|
|
15
16
|
type PatchOperation,
|
|
16
|
-
} from "
|
|
17
|
+
} from "../../direct/directPatch.js";
|
|
17
18
|
|
|
18
19
|
export interface ApplyPatchInput {
|
|
19
20
|
session_id: string;
|
|
@@ -33,6 +34,7 @@ export interface ApplyPatchOutput {
|
|
|
33
34
|
|
|
34
35
|
export function applyPatch(input: ApplyPatchInput): ApplyPatchOutput {
|
|
35
36
|
const config = getConfig();
|
|
37
|
+
return withDirectSessionMutationLock(input.session_id, () => {
|
|
36
38
|
|
|
37
39
|
// 1. Read session and guard active (not expired, not finalized)
|
|
38
40
|
const session = readDirectSession(input.session_id);
|
|
@@ -47,26 +49,45 @@ export function applyPatch(input: ApplyPatchInput): ApplyPatchOutput {
|
|
|
47
49
|
);
|
|
48
50
|
|
|
49
51
|
// 3. Calculate patch size and guard against directMaxPatchBytes
|
|
50
|
-
const patchBytes = JSON.stringify(input.operations)
|
|
52
|
+
const patchBytes = Buffer.byteLength(JSON.stringify(input.operations), "utf-8");
|
|
51
53
|
guardDirectPatchSize(patchBytes);
|
|
52
54
|
|
|
53
|
-
// 4.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
input.expected_sha256
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
// 4. Apply against the same bytes whose hash is validated. Re-resolve the
|
|
56
|
+
// target immediately before replacement to detect link/reparse-point swaps.
|
|
57
|
+
const patchResult = applyPatchOperations(resolvedPath, input.operations, {
|
|
58
|
+
expectedSha256: input.expected_sha256,
|
|
59
|
+
maxFileBytes: config.directMaxFileBytes,
|
|
60
|
+
revalidatePath: () => guardDirectWritePath(
|
|
61
|
+
input.path,
|
|
62
|
+
session.resolved_repo_path,
|
|
63
|
+
config.workspaceRoot,
|
|
64
|
+
),
|
|
65
|
+
});
|
|
66
|
+
const beforeSha256 = patchResult.before_sha256;
|
|
61
67
|
|
|
62
68
|
// Re-read the file from disk to compute the authoritative after_sha256
|
|
63
|
-
const
|
|
69
|
+
const finalPath = guardDirectWritePath(
|
|
70
|
+
input.path,
|
|
71
|
+
session.resolved_repo_path,
|
|
72
|
+
config.workspaceRoot,
|
|
73
|
+
);
|
|
74
|
+
const afterSha256 = computeFileSha256(finalPath);
|
|
75
|
+
if (afterSha256 !== patchResult.after_sha256) {
|
|
76
|
+
throw new PatchWardenError(
|
|
77
|
+
"direct_target_changed_after_write",
|
|
78
|
+
`Patch target "${input.path}" changed before the operation could be recorded.`,
|
|
79
|
+
"Inspect the file and retry only after concurrent filesystem changes stop.",
|
|
80
|
+
true,
|
|
81
|
+
{ path: input.path, expected_sha256: patchResult.after_sha256, actual_sha256: afterSha256 },
|
|
82
|
+
);
|
|
83
|
+
}
|
|
64
84
|
|
|
65
85
|
// 6. Append operation record to session
|
|
66
86
|
appendDirectSessionOperation(input.session_id, {
|
|
67
|
-
index:
|
|
87
|
+
index: 0,
|
|
68
88
|
timestamp: new Date().toISOString(),
|
|
69
89
|
path: input.path,
|
|
90
|
+
operation_type: "patch",
|
|
70
91
|
before_sha256: beforeSha256,
|
|
71
92
|
after_sha256: afterSha256,
|
|
72
93
|
operations_applied: patchResult.operations_applied,
|
|
@@ -83,4 +104,5 @@ export function applyPatch(input: ApplyPatchInput): ApplyPatchOutput {
|
|
|
83
104
|
next_action:
|
|
84
105
|
"Call run_verification to test the changes, or apply_patch for more edits.",
|
|
85
106
|
};
|
|
107
|
+
}, config);
|
|
86
108
|
}
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* 参考 src/tools/auditTask.ts 的导出/返回风格。
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { getConfig } from "
|
|
9
|
-
import { guardWorkspacePath } from "
|
|
10
|
-
import { discardWorktree } from "
|
|
8
|
+
import { getConfig } from "../../config.js";
|
|
9
|
+
import { guardWorkspacePath } from "../../security/pathGuard.js";
|
|
10
|
+
import { discardWorktree } from "../../goal/worktreeManager.js";
|
|
11
11
|
|
|
12
12
|
export interface DiscardWorktreeInput {
|
|
13
13
|
worktree_id: string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from "./androidDoctor.js";
|
|
2
|
+
export * from "./applyPatch.js";
|
|
3
|
+
export * from "./discardWorktree.js";
|
|
4
|
+
export * from "./listAgents.js";
|
|
5
|
+
export * from "./listWorkspace.js";
|
|
6
|
+
export * from "./mergeWorktree.js";
|
|
7
|
+
export * from "./readWorkspaceFile.js";
|
|
8
|
+
export * from "./recommendAgentForTask.js";
|
|
9
|
+
export * from "./searchWorkspace.js";
|
|
10
|
+
export * from "./syncFile.js";
|