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,245 +1,254 @@
|
|
|
1
|
-
import { writeFileSync } from "node:fs";
|
|
2
1
|
import { join, resolve, relative, isAbsolute } from "node:path";
|
|
3
|
-
import { getConfig } from "../config.js";
|
|
4
2
|
import { isSensitivePath } from "../security/sensitiveGuard.js";
|
|
5
|
-
import { readDirectSession, getDirectSessionDir, } from "./directSessionStore.js";
|
|
3
|
+
import { readDirectSession, getDirectSessionDir, updateDirectSession, withDirectSessionMutationLock, } from "./directSessionStore.js";
|
|
4
|
+
import { atomicWriteFileSync, atomicWriteJsonFileSync } from "../utils/atomicFile.js";
|
|
6
5
|
// ── Main audit function ────────────────────────────────────────────
|
|
7
6
|
export function auditDirectSession(sessionId) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
checks.push({
|
|
17
|
-
name: "session_finalized",
|
|
18
|
-
result: session.finalized ? "pass" : "fail",
|
|
19
|
-
detail: session.finalized
|
|
20
|
-
? "Session has been finalized."
|
|
21
|
-
: "Session has not been finalized. Call finalize_direct_session first.",
|
|
22
|
-
reason_code: session.finalized ? undefined : "session_not_finalized",
|
|
23
|
-
});
|
|
24
|
-
// Get change artifacts if available
|
|
25
|
-
const artifacts = session.change_artifacts;
|
|
26
|
-
// Check 2: diff empty
|
|
27
|
-
const changedFilesTotal = artifacts?.changed_files?.length ?? 0;
|
|
28
|
-
checks.push({
|
|
29
|
-
name: "diff_empty",
|
|
30
|
-
result: changedFilesTotal === 0 ? "warn" : "pass",
|
|
31
|
-
detail: changedFilesTotal === 0
|
|
32
|
-
? "No file changes detected in this session."
|
|
33
|
-
: `${changedFilesTotal} file(s) changed.`,
|
|
34
|
-
reason_code: changedFilesTotal === 0 ? "empty_diff" : undefined,
|
|
35
|
-
});
|
|
36
|
-
if (artifacts) {
|
|
37
|
-
// Check 3: out-of-scope changes
|
|
38
|
-
const outOfScope = findOutOfScopeChanges(artifacts, session.resolved_repo_path);
|
|
39
|
-
checks.push({
|
|
40
|
-
name: "out_of_scope_changes",
|
|
41
|
-
result: outOfScope.length === 0 ? "pass" : "fail",
|
|
42
|
-
detail: outOfScope.length === 0
|
|
43
|
-
? "All changes are within the session repo_path."
|
|
44
|
-
: `${outOfScope.length} file(s) modified outside session repo_path: ${outOfScope.join(", ")}`,
|
|
45
|
-
reason_code: outOfScope.length > 0 ? "out_of_scope_changes" : undefined,
|
|
46
|
-
});
|
|
47
|
-
// Check 4: sensitive files
|
|
48
|
-
const sensitiveFiles = findSensitiveChanges(artifacts);
|
|
7
|
+
return withDirectSessionMutationLock(sessionId, () => {
|
|
8
|
+
const session = readDirectSession(sessionId);
|
|
9
|
+
const sessionDir = getDirectSessionDir(sessionId);
|
|
10
|
+
const checks = [];
|
|
11
|
+
const reasonCodes = [];
|
|
12
|
+
const blockingFindings = [];
|
|
13
|
+
const warnings = [];
|
|
14
|
+
// Check 1: session finalized
|
|
49
15
|
checks.push({
|
|
50
|
-
name: "
|
|
51
|
-
result:
|
|
52
|
-
detail:
|
|
53
|
-
? "
|
|
54
|
-
:
|
|
55
|
-
reason_code:
|
|
16
|
+
name: "session_finalized",
|
|
17
|
+
result: session.finalized ? "pass" : "fail",
|
|
18
|
+
detail: session.finalized
|
|
19
|
+
? "Session has been finalized."
|
|
20
|
+
: "Session has not been finalized. Call finalize_direct_session first.",
|
|
21
|
+
reason_code: session.finalized ? undefined : "session_not_finalized",
|
|
56
22
|
});
|
|
57
|
-
//
|
|
58
|
-
const
|
|
23
|
+
// Get change artifacts if available
|
|
24
|
+
const artifacts = session.change_artifacts;
|
|
25
|
+
// Check 2: diff empty
|
|
26
|
+
const changedFilesTotal = artifacts?.changed_files?.length ?? 0;
|
|
59
27
|
checks.push({
|
|
60
|
-
name: "
|
|
61
|
-
result:
|
|
62
|
-
detail:
|
|
63
|
-
? "No
|
|
64
|
-
:
|
|
65
|
-
reason_code:
|
|
28
|
+
name: "diff_empty",
|
|
29
|
+
result: changedFilesTotal === 0 ? "warn" : "pass",
|
|
30
|
+
detail: changedFilesTotal === 0
|
|
31
|
+
? "No file changes detected in this session."
|
|
32
|
+
: `${changedFilesTotal} file(s) changed.`,
|
|
33
|
+
reason_code: changedFilesTotal === 0 ? "empty_diff" : undefined,
|
|
66
34
|
});
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
35
|
+
if (artifacts) {
|
|
36
|
+
// Check 3: out-of-scope changes
|
|
37
|
+
const outOfScope = findOutOfScopeChanges(artifacts, session.resolved_repo_path);
|
|
38
|
+
checks.push({
|
|
39
|
+
name: "out_of_scope_changes",
|
|
40
|
+
result: outOfScope.length === 0 ? "pass" : "fail",
|
|
41
|
+
detail: outOfScope.length === 0
|
|
42
|
+
? "All changes are within the session repo_path."
|
|
43
|
+
: `${outOfScope.length} file(s) modified outside session repo_path: ${outOfScope.join(", ")}`,
|
|
44
|
+
reason_code: outOfScope.length > 0 ? "out_of_scope_changes" : undefined,
|
|
45
|
+
});
|
|
46
|
+
// Check 4: sensitive files
|
|
47
|
+
const sensitiveFiles = findSensitiveChanges(artifacts);
|
|
48
|
+
checks.push({
|
|
49
|
+
name: "sensitive_file_access",
|
|
50
|
+
result: sensitiveFiles.length === 0 ? "pass" : "fail",
|
|
51
|
+
detail: sensitiveFiles.length === 0
|
|
52
|
+
? "No sensitive files modified."
|
|
53
|
+
: `Sensitive files modified: ${sensitiveFiles.join(", ")}`,
|
|
54
|
+
reason_code: sensitiveFiles.length > 0 ? "sensitive_file_modified" : undefined,
|
|
55
|
+
});
|
|
56
|
+
checks.push({
|
|
57
|
+
name: "sensitive_content",
|
|
58
|
+
result: artifacts.diff_redacted ? "fail" : "pass",
|
|
59
|
+
detail: artifacts.diff_redacted
|
|
60
|
+
? `Credential-like content was redacted from diff evidence (${(artifacts.diff_redaction_categories ?? []).join(", ") || "sensitive_content"}).`
|
|
61
|
+
: "No credential-like content was detected in diff evidence.",
|
|
62
|
+
reason_code: artifacts.diff_redacted ? "sensitive_content_detected" : undefined,
|
|
63
|
+
});
|
|
64
|
+
// Check 5: node_modules modification
|
|
65
|
+
const nodeModulesChanges = findPathChanges(artifacts, "node_modules");
|
|
66
|
+
checks.push({
|
|
67
|
+
name: "node_modules_modified",
|
|
68
|
+
result: nodeModulesChanges.length === 0 ? "pass" : "fail",
|
|
69
|
+
detail: nodeModulesChanges.length === 0
|
|
70
|
+
? "No node_modules modifications."
|
|
71
|
+
: `node_modules modified: ${nodeModulesChanges.join(", ")}`,
|
|
72
|
+
reason_code: nodeModulesChanges.length > 0 ? "node_modules_modified" : undefined,
|
|
73
|
+
});
|
|
74
|
+
// Check 6: release/dist modification
|
|
75
|
+
const releaseChanges = findPathChanges(artifacts, "release");
|
|
76
|
+
const distChanges = findPathChanges(artifacts, "dist");
|
|
77
|
+
const artifactDirChanges = [...releaseChanges, ...distChanges];
|
|
78
|
+
const hasBuildArtifacts = artifacts.artifact_hygiene.counts.tracked_build_artifacts > 0;
|
|
79
|
+
checks.push({
|
|
80
|
+
name: "release_dist_modified",
|
|
81
|
+
result: artifactDirChanges.length === 0
|
|
82
|
+
? "pass"
|
|
83
|
+
: hasBuildArtifacts
|
|
84
|
+
? "warn"
|
|
85
|
+
: "fail",
|
|
86
|
+
detail: artifactDirChanges.length === 0
|
|
87
|
+
? "No release/dist modifications."
|
|
88
|
+
: `release/dist modified: ${artifactDirChanges.join(", ")}${hasBuildArtifacts ? " (build-generated)" : " (not build-generated)"}`,
|
|
89
|
+
reason_code: artifactDirChanges.length > 0
|
|
90
|
+
? hasBuildArtifacts
|
|
91
|
+
? "build_artifact_modified"
|
|
92
|
+
: "release_dist_manually_modified"
|
|
93
|
+
: undefined,
|
|
94
|
+
});
|
|
95
|
+
// Check 7: file deletion
|
|
96
|
+
const deletedFiles = artifacts.changed_files.filter((f) => f.change === "deleted");
|
|
97
|
+
checks.push({
|
|
98
|
+
name: "file_deletion",
|
|
99
|
+
result: deletedFiles.length === 0 ? "pass" : "fail",
|
|
100
|
+
detail: deletedFiles.length === 0
|
|
101
|
+
? "No files deleted."
|
|
102
|
+
: `Files deleted: ${deletedFiles.map((f) => f.path).join(", ")}`,
|
|
103
|
+
reason_code: deletedFiles.length > 0 ? "file_deleted" : undefined,
|
|
104
|
+
});
|
|
105
|
+
// Check 8: file rename
|
|
106
|
+
const renamedFiles = artifacts.changed_files.filter((f) => f.change === "renamed");
|
|
107
|
+
checks.push({
|
|
108
|
+
name: "file_rename",
|
|
109
|
+
result: renamedFiles.length === 0 ? "pass" : "fail",
|
|
110
|
+
detail: renamedFiles.length === 0
|
|
111
|
+
? "No files renamed."
|
|
112
|
+
: `Files renamed: ${renamedFiles.map((f) => `${f.old_path} → ${f.path}`).join(", ")}`,
|
|
113
|
+
reason_code: renamedFiles.length > 0 ? "file_renamed" : undefined,
|
|
114
|
+
});
|
|
115
|
+
// Check 9: package-lock / dependency changes
|
|
116
|
+
const packageLockChanges = artifacts.changed_files.filter((f) => f.path === "package-lock.json" ||
|
|
117
|
+
f.path === "package.json" ||
|
|
118
|
+
f.path === "yarn.lock" ||
|
|
119
|
+
f.path === "pnpm-lock.yaml");
|
|
120
|
+
checks.push({
|
|
121
|
+
name: "dependency_changes",
|
|
122
|
+
result: packageLockChanges.length === 0 ? "pass" : "warn",
|
|
123
|
+
detail: packageLockChanges.length === 0
|
|
124
|
+
? "No dependency file changes."
|
|
125
|
+
: `Dependency files changed: ${packageLockChanges.map((f) => f.path).join(", ")}`,
|
|
126
|
+
reason_code: packageLockChanges.length > 0 ? "dependency_file_changed" : undefined,
|
|
127
|
+
});
|
|
128
|
+
// Check 13: title vs changed files consistency (warning only)
|
|
129
|
+
if (session.title && changedFilesTotal > 0) {
|
|
130
|
+
checks.push({
|
|
131
|
+
name: "title_change_consistency",
|
|
132
|
+
result: "pass",
|
|
133
|
+
detail: `Session title: "${session.title}", ${changedFilesTotal} file(s) changed. (Semantic check not performed - deterministic audit only.)`,
|
|
134
|
+
reason_code: undefined,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
checks.push({
|
|
139
|
+
name: "title_change_consistency",
|
|
140
|
+
result: "pass",
|
|
141
|
+
detail: "No title provided or no changes to compare.",
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
// Check 14: suspicious changes
|
|
145
|
+
const suspiciousCount = artifacts.artifact_hygiene.counts.suspicious_changes;
|
|
146
|
+
checks.push({
|
|
147
|
+
name: "suspicious_changes",
|
|
148
|
+
result: suspiciousCount === 0 ? "pass" : "warn",
|
|
149
|
+
detail: suspiciousCount === 0
|
|
150
|
+
? "No suspicious changes detected."
|
|
151
|
+
: `${suspiciousCount} suspicious change(s) detected.`,
|
|
152
|
+
reason_code: suspiciousCount > 0 ? "suspicious_changes" : undefined,
|
|
153
|
+
});
|
|
154
|
+
// Check 15: runtime generated files
|
|
155
|
+
const runtimeCount = artifacts.artifact_hygiene.counts.runtime_generated_files;
|
|
156
|
+
checks.push({
|
|
157
|
+
name: "runtime_generated_files",
|
|
158
|
+
result: runtimeCount === 0 ? "pass" : "warn",
|
|
159
|
+
detail: runtimeCount === 0
|
|
160
|
+
? "No runtime-generated files detected."
|
|
161
|
+
: `${runtimeCount} runtime-generated file(s) detected.`,
|
|
162
|
+
reason_code: runtimeCount > 0 ? "runtime_generated_files" : undefined,
|
|
163
|
+
});
|
|
164
|
+
// Check 16: tracked build artifacts
|
|
165
|
+
const buildArtifactCount = artifacts.artifact_hygiene.counts.tracked_build_artifacts;
|
|
166
|
+
checks.push({
|
|
167
|
+
name: "tracked_build_artifacts",
|
|
168
|
+
result: buildArtifactCount === 0 ? "pass" : "warn",
|
|
169
|
+
detail: buildArtifactCount === 0
|
|
170
|
+
? "No tracked build artifacts detected."
|
|
171
|
+
: `${buildArtifactCount} tracked build artifact(s) detected.`,
|
|
172
|
+
reason_code: buildArtifactCount > 0 ? "tracked_build_artifacts" : undefined,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
// Check 10: at least one verification command run
|
|
176
|
+
const verificationRuns = session.verification_runs || [];
|
|
177
|
+
const hasVerification = verificationRuns.length > 0;
|
|
178
|
+
const hasSourceChanges = artifacts
|
|
179
|
+
? artifacts.changed_files.some((f) => f.kind === "source")
|
|
180
|
+
: false;
|
|
72
181
|
checks.push({
|
|
73
|
-
name: "
|
|
74
|
-
result:
|
|
182
|
+
name: "verification_run",
|
|
183
|
+
result: hasVerification
|
|
75
184
|
? "pass"
|
|
76
|
-
:
|
|
77
|
-
? "
|
|
78
|
-
: "
|
|
79
|
-
detail:
|
|
80
|
-
?
|
|
81
|
-
:
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
185
|
+
: hasSourceChanges
|
|
186
|
+
? "fail"
|
|
187
|
+
: "warn",
|
|
188
|
+
detail: hasVerification
|
|
189
|
+
? `${verificationRuns.length} verification command(s) run.`
|
|
190
|
+
: hasSourceChanges
|
|
191
|
+
? "Source files were modified but no verification commands were run."
|
|
192
|
+
: "No verification commands were run (no source changes detected).",
|
|
193
|
+
reason_code: !hasVerification
|
|
194
|
+
? hasSourceChanges
|
|
195
|
+
? "source_changes_without_verification"
|
|
196
|
+
: "no_verification_run"
|
|
86
197
|
: undefined,
|
|
87
198
|
});
|
|
88
|
-
// Check
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
name: "file_deletion",
|
|
92
|
-
result: deletedFiles.length === 0 ? "pass" : "fail",
|
|
93
|
-
detail: deletedFiles.length === 0
|
|
94
|
-
? "No files deleted."
|
|
95
|
-
: `Files deleted: ${deletedFiles.map((f) => f.path).join(", ")}`,
|
|
96
|
-
reason_code: deletedFiles.length > 0 ? "file_deleted" : undefined,
|
|
97
|
-
});
|
|
98
|
-
// Check 8: file rename
|
|
99
|
-
const renamedFiles = artifacts.changed_files.filter((f) => f.change === "renamed");
|
|
100
|
-
checks.push({
|
|
101
|
-
name: "file_rename",
|
|
102
|
-
result: renamedFiles.length === 0 ? "pass" : "fail",
|
|
103
|
-
detail: renamedFiles.length === 0
|
|
104
|
-
? "No files renamed."
|
|
105
|
-
: `Files renamed: ${renamedFiles.map((f) => `${f.old_path} → ${f.path}`).join(", ")}`,
|
|
106
|
-
reason_code: renamedFiles.length > 0 ? "file_renamed" : undefined,
|
|
107
|
-
});
|
|
108
|
-
// Check 9: package-lock / dependency changes
|
|
109
|
-
const packageLockChanges = artifacts.changed_files.filter((f) => f.path === "package-lock.json" ||
|
|
110
|
-
f.path === "package.json" ||
|
|
111
|
-
f.path === "yarn.lock" ||
|
|
112
|
-
f.path === "pnpm-lock.yaml");
|
|
113
|
-
checks.push({
|
|
114
|
-
name: "dependency_changes",
|
|
115
|
-
result: packageLockChanges.length === 0 ? "pass" : "warn",
|
|
116
|
-
detail: packageLockChanges.length === 0
|
|
117
|
-
? "No dependency file changes."
|
|
118
|
-
: `Dependency files changed: ${packageLockChanges.map((f) => f.path).join(", ")}`,
|
|
119
|
-
reason_code: packageLockChanges.length > 0 ? "dependency_file_changed" : undefined,
|
|
120
|
-
});
|
|
121
|
-
// Check 13: title vs changed files consistency (warning only)
|
|
122
|
-
if (session.title && changedFilesTotal > 0) {
|
|
199
|
+
// Check 11: verification commands passed
|
|
200
|
+
if (hasVerification) {
|
|
201
|
+
const allPassed = verificationRuns.every((r) => r.passed);
|
|
123
202
|
checks.push({
|
|
124
|
-
name: "
|
|
125
|
-
result: "pass",
|
|
126
|
-
detail:
|
|
127
|
-
|
|
203
|
+
name: "verification_passed",
|
|
204
|
+
result: allPassed ? "pass" : "fail",
|
|
205
|
+
detail: allPassed
|
|
206
|
+
? "All verification commands passed."
|
|
207
|
+
: `${verificationRuns.filter((r) => !r.passed).length} verification command(s) failed.`,
|
|
208
|
+
reason_code: !allPassed ? "verification_failed" : undefined,
|
|
128
209
|
});
|
|
129
210
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
211
|
+
// Collect results
|
|
212
|
+
for (const check of checks) {
|
|
213
|
+
if (check.reason_code)
|
|
214
|
+
reasonCodes.push(check.reason_code);
|
|
215
|
+
if (check.result === "fail")
|
|
216
|
+
blockingFindings.push(`${check.name}: ${check.detail}`);
|
|
217
|
+
if (check.result === "warn")
|
|
218
|
+
warnings.push(`${check.name}: ${check.detail}`);
|
|
136
219
|
}
|
|
137
|
-
//
|
|
138
|
-
const
|
|
139
|
-
checks.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
:
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
// Check 10: at least one verification command run
|
|
169
|
-
const verificationRuns = session.verification_runs || [];
|
|
170
|
-
const hasVerification = verificationRuns.length > 0;
|
|
171
|
-
const hasSourceChanges = artifacts
|
|
172
|
-
? artifacts.changed_files.some((f) => f.kind === "source")
|
|
173
|
-
: false;
|
|
174
|
-
checks.push({
|
|
175
|
-
name: "verification_run",
|
|
176
|
-
result: hasVerification
|
|
177
|
-
? "pass"
|
|
178
|
-
: hasSourceChanges
|
|
179
|
-
? "fail"
|
|
180
|
-
: "warn",
|
|
181
|
-
detail: hasVerification
|
|
182
|
-
? `${verificationRuns.length} verification command(s) run.`
|
|
183
|
-
: hasSourceChanges
|
|
184
|
-
? "Source files were modified but no verification commands were run."
|
|
185
|
-
: "No verification commands were run (no source changes detected).",
|
|
186
|
-
reason_code: !hasVerification
|
|
187
|
-
? hasSourceChanges
|
|
188
|
-
? "source_changes_without_verification"
|
|
189
|
-
: "no_verification_run"
|
|
190
|
-
: undefined,
|
|
220
|
+
// Determine decision
|
|
221
|
+
const hasFail = checks.some((c) => c.result === "fail");
|
|
222
|
+
const hasWarn = checks.some((c) => c.result === "warn");
|
|
223
|
+
const decision = hasFail ? "fail" : hasWarn ? "warn" : "pass";
|
|
224
|
+
const diffPath = join(sessionDir, "diff.patch");
|
|
225
|
+
const summaryPath = join(sessionDir, "summary.md");
|
|
226
|
+
const auditPath = join(sessionDir, "audit.md");
|
|
227
|
+
const output = {
|
|
228
|
+
session_id: sessionId,
|
|
229
|
+
decision,
|
|
230
|
+
reason_codes: reasonCodes,
|
|
231
|
+
blocking_findings: blockingFindings,
|
|
232
|
+
warnings,
|
|
233
|
+
evidence: {
|
|
234
|
+
changed_files_total: changedFilesTotal,
|
|
235
|
+
verification_runs: verificationRuns,
|
|
236
|
+
diff_path: diffPath,
|
|
237
|
+
summary_path: summaryPath,
|
|
238
|
+
audit_path: auditPath,
|
|
239
|
+
},
|
|
240
|
+
next_action: decision === "pass"
|
|
241
|
+
? "Audit passed. Changes are safe to accept."
|
|
242
|
+
: decision === "warn"
|
|
243
|
+
? "Audit completed with warnings. Review the warnings before accepting changes."
|
|
244
|
+
: "Audit failed. Review the blocking findings and create a new session to fix issues.",
|
|
245
|
+
};
|
|
246
|
+
// Write audit.json and audit.md
|
|
247
|
+
atomicWriteJsonFileSync(join(sessionDir, "audit.json"), output);
|
|
248
|
+
atomicWriteFileSync(auditPath, formatAuditMd(output, checks, session));
|
|
249
|
+
updateDirectSession(sessionId, { audited: true });
|
|
250
|
+
return output;
|
|
191
251
|
});
|
|
192
|
-
// Check 11: verification commands passed
|
|
193
|
-
if (hasVerification) {
|
|
194
|
-
const allPassed = verificationRuns.every((r) => r.passed);
|
|
195
|
-
checks.push({
|
|
196
|
-
name: "verification_passed",
|
|
197
|
-
result: allPassed ? "pass" : "fail",
|
|
198
|
-
detail: allPassed
|
|
199
|
-
? "All verification commands passed."
|
|
200
|
-
: `${verificationRuns.filter((r) => !r.passed).length} verification command(s) failed.`,
|
|
201
|
-
reason_code: !allPassed ? "verification_failed" : undefined,
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
// Collect results
|
|
205
|
-
for (const check of checks) {
|
|
206
|
-
if (check.reason_code)
|
|
207
|
-
reasonCodes.push(check.reason_code);
|
|
208
|
-
if (check.result === "fail")
|
|
209
|
-
blockingFindings.push(`${check.name}: ${check.detail}`);
|
|
210
|
-
if (check.result === "warn")
|
|
211
|
-
warnings.push(`${check.name}: ${check.detail}`);
|
|
212
|
-
}
|
|
213
|
-
// Determine decision
|
|
214
|
-
const hasFail = checks.some((c) => c.result === "fail");
|
|
215
|
-
const hasWarn = checks.some((c) => c.result === "warn");
|
|
216
|
-
const decision = hasFail ? "fail" : hasWarn ? "warn" : "pass";
|
|
217
|
-
const diffPath = join(sessionDir, "diff.patch");
|
|
218
|
-
const summaryPath = join(sessionDir, "summary.md");
|
|
219
|
-
const auditPath = join(sessionDir, "audit.md");
|
|
220
|
-
const output = {
|
|
221
|
-
session_id: sessionId,
|
|
222
|
-
decision,
|
|
223
|
-
reason_codes: reasonCodes,
|
|
224
|
-
blocking_findings: blockingFindings,
|
|
225
|
-
warnings,
|
|
226
|
-
evidence: {
|
|
227
|
-
changed_files_total: changedFilesTotal,
|
|
228
|
-
verification_runs: verificationRuns,
|
|
229
|
-
diff_path: diffPath,
|
|
230
|
-
summary_path: summaryPath,
|
|
231
|
-
audit_path: auditPath,
|
|
232
|
-
},
|
|
233
|
-
next_action: decision === "pass"
|
|
234
|
-
? "Audit passed. Changes are safe to accept."
|
|
235
|
-
: decision === "warn"
|
|
236
|
-
? "Audit completed with warnings. Review the warnings before accepting changes."
|
|
237
|
-
: "Audit failed. Review the blocking findings and create a new session to fix issues.",
|
|
238
|
-
};
|
|
239
|
-
// Write audit.json and audit.md
|
|
240
|
-
writeFileSync(join(sessionDir, "audit.json"), JSON.stringify(output, null, 2), "utf-8");
|
|
241
|
-
writeFileSync(auditPath, formatAuditMd(output, checks, session), "utf-8");
|
|
242
|
-
return output;
|
|
243
252
|
}
|
|
244
253
|
// ── Helper functions ───────────────────────────────────────────────
|
|
245
254
|
function findOutOfScopeChanges(artifacts, resolvedRepoPath) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type PatchWardenConfig } from "../config.js";
|
|
1
2
|
import type { DirectSessionRecord } from "./directSessionStore.js";
|
|
2
3
|
export declare function guardDirectSessionActive(session: DirectSessionRecord): void;
|
|
3
4
|
export declare function guardDirectSessionFinalized(session: DirectSessionRecord): void;
|
|
@@ -16,5 +17,5 @@ export declare function guardDirectReadPath(filePath: string, resolvedRepoPath:
|
|
|
16
17
|
*/
|
|
17
18
|
export declare function guardDirectWritePath(filePath: string, resolvedRepoPath: string, workspaceRoot: string): string;
|
|
18
19
|
export declare function guardDirectPatchSize(patchBytes: number): void;
|
|
19
|
-
export declare function guardDirectFileSize(fileBytes: number): void;
|
|
20
|
+
export declare function guardDirectFileSize(fileBytes: number, config?: PatchWardenConfig): void;
|
|
20
21
|
export declare function isBinaryFile(filePath: string): boolean;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { relative } from "node:path";
|
|
2
2
|
import { existsSync, statSync, openSync, readSync, closeSync } from "node:fs";
|
|
3
3
|
import { PatchWardenError } from "../errors.js";
|
|
4
|
-
import { isSensitivePath } from "../security/sensitiveGuard.js";
|
|
4
|
+
import { hasWindowsAlternateDataStream, isSensitivePath, } from "../security/sensitiveGuard.js";
|
|
5
|
+
import { guardPath, guardWorkspacePath } from "../security/pathGuard.js";
|
|
5
6
|
import { getConfig } from "../config.js";
|
|
6
7
|
// ── Session state guards ───────────────────────────────────────────
|
|
7
8
|
export function guardDirectSessionActive(session) {
|
|
@@ -23,34 +24,34 @@ export function guardDirectSessionFinalized(session) {
|
|
|
23
24
|
* inside both the repo and the workspace root.
|
|
24
25
|
*/
|
|
25
26
|
export function guardDirectPath(filePath, resolvedRepoPath, workspaceRoot) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
27
|
+
if (hasWindowsAlternateDataStream(filePath)) {
|
|
28
|
+
throw new PatchWardenError("windows_ads_path_blocked", `Path "${filePath}" contains a Windows alternate data stream suffix.`, "Use a normal relative file path without a colon or NTFS stream name.", true, { path: filePath, operation: "direct_path_access" });
|
|
29
|
+
}
|
|
30
|
+
// Resolve the repo first so a repo-level symlink/junction cannot escape the
|
|
31
|
+
// workspace. guardPath then resolves the candidate or its nearest existing
|
|
32
|
+
// parent, closing intermediate symlink/junction escapes for new files too.
|
|
33
|
+
try {
|
|
34
|
+
const guardedRepo = guardWorkspacePath(resolvedRepoPath, workspaceRoot);
|
|
35
|
+
return guardPath(filePath, guardedRepo);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (error instanceof PatchWardenError && error.reason === "workspace_path_escape") {
|
|
39
|
+
throw new PatchWardenError("path_outside_repo", `Path "${filePath}" resolves outside the session repo.`, "Use a relative path inside the session's repo_path.", true, { path: filePath, operation: "direct_path_access" });
|
|
40
|
+
}
|
|
41
|
+
throw error;
|
|
39
42
|
}
|
|
40
|
-
return resolved;
|
|
41
43
|
}
|
|
42
44
|
/**
|
|
43
45
|
* Guard a read path: must be inside repo, not sensitive, not binary.
|
|
44
46
|
*/
|
|
45
47
|
export function guardDirectReadPath(filePath, resolvedRepoPath, workspaceRoot) {
|
|
46
48
|
const resolved = guardDirectPath(filePath, resolvedRepoPath, workspaceRoot);
|
|
47
|
-
const
|
|
49
|
+
const policyPaths = directPolicyPaths(filePath, resolved, resolvedRepoPath, workspaceRoot);
|
|
48
50
|
// Block internal PatchWarden paths (sessions, tasks, plans, assessments)
|
|
49
|
-
if (
|
|
50
|
-
normalized.includes("/.patchwarden/")) {
|
|
51
|
+
if (policyPaths.some((path) => hasPathSegment(path, ".patchwarden"))) {
|
|
51
52
|
throw new PatchWardenError("internal_patchwarden_path_blocked", `Access denied: "${filePath}" is inside the internal .patchwarden directory.`, "Internal PatchWarden files cannot be accessed through Direct mode.", true, { path: filePath, operation: "direct_read" });
|
|
52
53
|
}
|
|
53
|
-
if (isSensitivePath(
|
|
54
|
+
if (policyPaths.some((path) => isSensitivePath(path))) {
|
|
54
55
|
throw new PatchWardenError("sensitive_path_blocked", `Access denied: "${filePath}" matches a sensitive file pattern.`, "Read only non-sensitive files within the session repo.", true, { path: filePath, operation: "direct_read" });
|
|
55
56
|
}
|
|
56
57
|
if (existsSync(resolved) && statSync(resolved).isFile() && isBinaryFile(resolved)) {
|
|
@@ -64,25 +65,24 @@ export function guardDirectReadPath(filePath, resolvedRepoPath, workspaceRoot) {
|
|
|
64
65
|
*/
|
|
65
66
|
export function guardDirectWritePath(filePath, resolvedRepoPath, workspaceRoot) {
|
|
66
67
|
const resolved = guardDirectPath(filePath, resolvedRepoPath, workspaceRoot);
|
|
67
|
-
const
|
|
68
|
+
const policyPaths = directPolicyPaths(filePath, resolved, resolvedRepoPath, workspaceRoot);
|
|
68
69
|
// Block internal PatchWarden paths
|
|
69
|
-
if (
|
|
70
|
-
normalized.includes("/.patchwarden/")) {
|
|
70
|
+
if (policyPaths.some((path) => hasPathSegment(path, ".patchwarden"))) {
|
|
71
71
|
throw new PatchWardenError("internal_patchwarden_path_blocked", `Modification denied: "${filePath}" is inside the internal .patchwarden directory.`, "Internal PatchWarden files cannot be modified through Direct mode.", true, { path: filePath, operation: "direct_write" });
|
|
72
72
|
}
|
|
73
|
-
if (isSensitivePath(
|
|
73
|
+
if (policyPaths.some((path) => isSensitivePath(path))) {
|
|
74
74
|
throw new PatchWardenError("sensitive_path_blocked", `Modification denied: "${filePath}" matches a sensitive file pattern.`, "Do not modify .env, credentials, keys, or other sensitive files.", true, { path: filePath, operation: "direct_write" });
|
|
75
75
|
}
|
|
76
76
|
// Block node_modules
|
|
77
|
-
if (
|
|
77
|
+
if (policyPaths.some((path) => hasPathSegment(path, "node_modules"))) {
|
|
78
78
|
throw new PatchWardenError("blocked_artifact_path", `Modification denied: "${filePath}" is inside node_modules.`, "Do not modify dependencies directly. Use package management tools instead.", true, { path: filePath, operation: "direct_write" });
|
|
79
79
|
}
|
|
80
80
|
// Block release/
|
|
81
|
-
if (
|
|
81
|
+
if (policyPaths.some((path) => hasPathSegment(path, "release"))) {
|
|
82
82
|
throw new PatchWardenError("blocked_artifact_path", `Modification denied: "${filePath}" is inside the release directory.`, "Do not manually modify release artifacts.", true, { path: filePath, operation: "direct_write" });
|
|
83
83
|
}
|
|
84
84
|
// Block dist/
|
|
85
|
-
if (
|
|
85
|
+
if (policyPaths.some((path) => hasPathSegment(path, "dist"))) {
|
|
86
86
|
throw new PatchWardenError("blocked_artifact_path", `Modification denied: "${filePath}" is inside the dist directory.`, "Do not manually modify build output. Modify source files instead.", true, { path: filePath, operation: "direct_write" });
|
|
87
87
|
}
|
|
88
88
|
if (existsSync(resolved) && statSync(resolved).isFile() && isBinaryFile(resolved)) {
|
|
@@ -90,6 +90,20 @@ export function guardDirectWritePath(filePath, resolvedRepoPath, workspaceRoot)
|
|
|
90
90
|
}
|
|
91
91
|
return resolved;
|
|
92
92
|
}
|
|
93
|
+
function directPolicyPaths(requestedPath, resolvedPath, resolvedRepoPath, workspaceRoot) {
|
|
94
|
+
const guardedRepo = guardWorkspacePath(resolvedRepoPath, workspaceRoot);
|
|
95
|
+
return [
|
|
96
|
+
requestedPath.replace(/\\/g, "/"),
|
|
97
|
+
relative(guardedRepo, resolvedPath).replace(/\\/g, "/"),
|
|
98
|
+
];
|
|
99
|
+
}
|
|
100
|
+
function hasPathSegment(filePath, expectedSegment) {
|
|
101
|
+
const expected = expectedSegment.toLowerCase();
|
|
102
|
+
return filePath
|
|
103
|
+
.split("/")
|
|
104
|
+
.filter(Boolean)
|
|
105
|
+
.some((segment) => segment.toLowerCase() === expected);
|
|
106
|
+
}
|
|
93
107
|
// ── Size guards ────────────────────────────────────────────────────
|
|
94
108
|
export function guardDirectPatchSize(patchBytes) {
|
|
95
109
|
const config = getConfig();
|
|
@@ -97,8 +111,7 @@ export function guardDirectPatchSize(patchBytes) {
|
|
|
97
111
|
throw new PatchWardenError("patch_too_large", `Patch size ${patchBytes} bytes exceeds maximum ${config.directMaxPatchBytes} bytes.`, "Split the patch into smaller operations or reduce the patch size.", true, { patch_bytes: patchBytes, max_bytes: config.directMaxPatchBytes });
|
|
98
112
|
}
|
|
99
113
|
}
|
|
100
|
-
export function guardDirectFileSize(fileBytes) {
|
|
101
|
-
const config = getConfig();
|
|
114
|
+
export function guardDirectFileSize(fileBytes, config = getConfig()) {
|
|
102
115
|
if (fileBytes > config.directMaxFileBytes) {
|
|
103
116
|
throw new PatchWardenError("file_too_large", `File size ${fileBytes} bytes exceeds maximum ${config.directMaxFileBytes} bytes.`, "Use search_workspace to find specific content, or split the read into smaller portions.", true, { file_bytes: fileBytes, max_bytes: config.directMaxFileBytes });
|
|
104
117
|
}
|
|
@@ -13,5 +13,10 @@ export interface ApplyPatchResult {
|
|
|
13
13
|
}
|
|
14
14
|
export declare function computeFileSha256(filePath: string): string;
|
|
15
15
|
export declare function computeContentSha256(content: string): string;
|
|
16
|
+
export interface ApplyPatchOptions {
|
|
17
|
+
expectedSha256?: string;
|
|
18
|
+
maxFileBytes?: number;
|
|
19
|
+
revalidatePath?: () => string;
|
|
20
|
+
}
|
|
16
21
|
export declare function validateExpectedSha256(filePath: string, expectedSha256: string): string;
|
|
17
|
-
export declare function applyPatchOperations(filePath: string, operations: PatchOperation[]): ApplyPatchResult;
|
|
22
|
+
export declare function applyPatchOperations(filePath: string, operations: PatchOperation[], options?: ApplyPatchOptions): ApplyPatchResult;
|