patchwarden 1.5.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PatchWarden.cmd +32 -2
- package/README.en.md +194 -26
- package/README.md +160 -22
- package/dist/assessments/agentAssessor.d.ts +1 -1
- package/dist/assessments/agentAssessor.js +24 -15
- package/dist/assessments/assessmentStore.d.ts +1 -1
- package/dist/assessments/assessmentStore.js +44 -36
- package/dist/assessments/confirmCli.js +5 -4
- package/dist/config.d.ts +22 -0
- package/dist/config.js +98 -11
- package/dist/control/fileManager.d.ts +2 -0
- package/dist/control/fileManager.js +39 -0
- package/dist/control/middleware/auth.d.ts +16 -0
- package/dist/control/middleware/auth.js +17 -0
- package/dist/control/middleware/static.d.ts +3 -0
- package/dist/control/middleware/static.js +78 -0
- package/dist/control/routeTable.d.ts +24 -0
- package/dist/control/routeTable.js +263 -0
- package/dist/control/routes/audit.d.ts +15 -0
- package/dist/control/routes/audit.js +287 -0
- package/dist/control/routes/evidence.d.ts +4 -0
- package/dist/control/routes/evidence.js +96 -0
- package/dist/control/routes/lineage.d.ts +3 -0
- package/dist/control/routes/lineage.js +71 -0
- package/dist/control/routes/policy.d.ts +3 -0
- package/dist/control/routes/policy.js +81 -0
- package/dist/control/routes/process.d.ts +12 -0
- package/dist/control/routes/process.js +487 -0
- package/dist/control/routes/sessions.d.ts +30 -0
- package/dist/control/routes/sessions.js +287 -0
- package/dist/control/routes/status.d.ts +51 -0
- package/dist/control/routes/status.js +307 -0
- package/dist/control/routes/taskActions.d.ts +21 -0
- package/dist/control/routes/taskActions.js +197 -0
- package/dist/control/routes/tasks.d.ts +24 -0
- package/dist/control/routes/tasks.js +309 -0
- package/dist/control/routes/workspace.d.ts +15 -0
- package/dist/control/routes/workspace.js +220 -0
- package/dist/control/runtime.d.ts +92 -0
- package/dist/control/runtime.js +425 -0
- package/dist/control/server.d.ts +13 -0
- package/dist/control/server.js +150 -0
- package/dist/control/shared.d.ts +59 -0
- package/dist/control/shared.js +341 -0
- package/dist/controlCenter.d.ts +6 -0
- package/dist/controlCenter.js +7 -2197
- package/dist/direct/directAudit.js +234 -225
- package/dist/direct/directGuards.d.ts +2 -1
- package/dist/direct/directGuards.js +72 -37
- package/dist/direct/directPatch.d.ts +6 -1
- package/dist/direct/directPatch.js +55 -8
- package/dist/direct/directSessionStore.d.ts +16 -6
- package/dist/direct/directSessionStore.js +127 -35
- package/dist/direct/directVerification.js +8 -16
- package/dist/doctor.d.ts +18 -1
- package/dist/doctor.js +614 -373
- package/dist/goal/acceptanceEngine.d.ts +1 -1
- package/dist/goal/goalProgress.js +75 -69
- package/dist/goal/goalReport.d.ts +54 -0
- package/dist/goal/goalReport.js +197 -0
- package/dist/goal/goalStatus.d.ts +8 -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.d.ts +63 -0
- package/dist/goal/specKitImport.js +221 -0
- package/dist/goal/subgoalSync.js +13 -10
- package/dist/goal/worktreeManager.d.ts +2 -0
- package/dist/goal/worktreeManager.js +209 -136
- package/dist/httpServer.js +126 -44
- package/dist/index.js +7 -4
- package/dist/logging.d.ts +7 -1
- package/dist/logging.js +13 -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 +14 -6
- package/dist/runner/changeCapture.js +287 -81
- package/dist/runner/cli.js +7 -6
- package/dist/runner/postTaskCleanup.js +60 -7
- package/dist/runner/processSecurity.d.ts +56 -0
- package/dist/runner/processSecurity.js +373 -0
- package/dist/runner/runTask.d.ts +1 -1
- package/dist/runner/runTask.js +339 -287
- package/dist/runner/simpleProcess.d.ts +3 -0
- package/dist/runner/simpleProcess.js +52 -38
- package/dist/{taskProgress.d.ts → runner/taskProgress.d.ts} +1 -1
- package/dist/{taskProgress.js → runner/taskProgress.js} +2 -2
- package/dist/{taskRuntime.d.ts → runner/taskRuntime.d.ts} +1 -1
- package/dist/{taskRuntime.js → runner/taskRuntime.js} +3 -2
- package/dist/runner/taskStatusStore.d.ts +11 -0
- package/dist/runner/taskStatusStore.js +24 -0
- package/dist/runner/watch.d.ts +14 -1
- package/dist/runner/watch.js +265 -54
- package/dist/security/contentRedaction.d.ts +6 -0
- package/dist/security/contentRedaction.js +24 -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 +280 -273
- 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} +28 -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} +8 -63
- package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +111 -37
- package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +6 -6
- package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +10 -10
- 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} +14 -10
- package/dist/tools/{safeViews.js → diagnostics/safeViews.js} +25 -15
- 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.d.ts → direct/createDirectSession.d.ts} +1 -1
- package/dist/tools/{createDirectSession.js → direct/createDirectSession.js} +8 -8
- package/dist/tools/{finalizeDirectSession.d.ts → direct/finalizeDirectSession.d.ts} +2 -2
- 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.d.ts +9 -0
- package/dist/tools/dispatch/coreDispatch.js +283 -0
- package/dist/tools/dispatch/diagnosticDispatch.d.ts +12 -0
- package/dist/tools/dispatch/diagnosticDispatch.js +80 -0
- package/dist/tools/dispatch/directDispatch.d.ts +8 -0
- package/dist/tools/dispatch/directDispatch.js +116 -0
- package/dist/tools/dispatch/goalDispatch.d.ts +8 -0
- package/dist/tools/dispatch/goalDispatch.js +92 -0
- package/dist/tools/dispatch/releaseDispatch.d.ts +8 -0
- package/dist/tools/dispatch/releaseDispatch.js +46 -0
- package/dist/tools/dispatch/types.d.ts +23 -0
- package/dist/tools/dispatch/types.js +15 -0
- 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} +2 -2
- 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 +10 -14
- package/dist/tools/registry.js +47 -1651
- package/dist/tools/{checkReleaseGate.d.ts → release/checkReleaseGate.d.ts} +1 -1
- package/dist/tools/{checkReleaseGate.js → release/checkReleaseGate.js} +3 -3
- package/dist/tools/release/index.d.ts +2 -0
- package/dist/tools/release/index.js +2 -0
- package/dist/tools/{releaseMode.d.ts → release/releaseMode.d.ts} +2 -2
- package/dist/tools/{releaseMode.js → release/releaseMode.js} +46 -26
- package/dist/tools/tasks/cancelTask.d.ts +11 -0
- package/dist/tools/tasks/cancelTask.js +85 -0
- package/dist/tools/{createTask.d.ts → tasks/createTask.d.ts} +12 -4
- package/dist/tools/{createTask.js → tasks/createTask.js} +46 -31
- package/dist/tools/{diagnoseTask.d.ts → tasks/diagnoseTask.d.ts} +1 -1
- package/dist/tools/{diagnoseTask.js → tasks/diagnoseTask.js} +8 -8
- package/dist/tools/{evidencePack.d.ts → tasks/evidencePack.d.ts} +6 -0
- package/dist/tools/tasks/evidencePack.js +387 -0
- package/dist/tools/{getTaskFile.js → tasks/getTaskFile.js} +4 -4
- package/dist/tools/{getTaskStatus.d.ts → tasks/getTaskStatus.d.ts} +6 -1
- package/dist/tools/{getTaskStatus.js → tasks/getTaskStatus.js} +5 -5
- package/dist/tools/{getTaskStdoutTail.js → tasks/getTaskStdoutTail.js} +13 -11
- package/dist/tools/{getTaskSummary.js → tasks/getTaskSummary.js} +33 -26
- package/dist/tools/tasks/index.d.ts +18 -0
- package/dist/tools/tasks/index.js +18 -0
- package/dist/tools/tasks/killTask.d.ts +1 -0
- package/dist/tools/{listTasks.d.ts → tasks/listTasks.d.ts} +1 -1
- package/dist/tools/{listTasks.js → tasks/listTasks.js} +3 -3
- package/dist/tools/{reconcileTasks.d.ts → tasks/reconcileTasks.d.ts} +1 -1
- package/dist/tools/{reconcileTasks.js → tasks/reconcileTasks.js} +64 -66
- package/dist/tools/{retryTask.d.ts → tasks/retryTask.d.ts} +2 -2
- 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} +59 -31
- package/dist/tools/tasks/runVerification.js +38 -0
- package/dist/tools/{taskLineage.js → tasks/taskLineage.js} +8 -7
- package/dist/tools/{taskOutputs.js → tasks/taskOutputs.js} +17 -16
- package/dist/tools/{androidDoctor.js → workspace/androidDoctor.js} +16 -10
- package/dist/tools/{applyPatch.d.ts → workspace/applyPatch.d.ts} +1 -1
- package/dist/tools/workspace/applyPatch.js +53 -0
- package/dist/tools/{discardWorktree.js → workspace/discardWorktree.js} +3 -3
- package/dist/tools/workspace/index.d.ts +10 -0
- package/dist/tools/workspace/index.js +10 -0
- package/dist/tools/{listAgents.d.ts → workspace/listAgents.d.ts} +5 -0
- package/dist/tools/{listAgents.js → workspace/listAgents.js} +8 -4
- package/dist/tools/{listWorkspace.js → workspace/listWorkspace.js} +3 -3
- package/dist/tools/{mergeWorktree.js → workspace/mergeWorktree.js} +3 -3
- package/dist/tools/{readWorkspaceFile.js → workspace/readWorkspaceFile.js} +8 -8
- package/dist/tools/{recommendAgentForTask.js → workspace/recommendAgentForTask.js} +3 -3
- package/dist/tools/{searchWorkspace.js → workspace/searchWorkspace.js} +5 -5
- package/dist/tools/{syncFile.d.ts → workspace/syncFile.d.ts} +1 -1
- package/dist/tools/workspace/syncFile.js +134 -0
- package/dist/utils/atomicFile.d.ts +7 -0
- package/dist/utils/atomicFile.js +48 -0
- package/dist/utils/boundedFile.d.ts +8 -0
- package/dist/utils/boundedFile.js +93 -0
- package/dist/utils/lockedJsonFile.d.ts +15 -0
- package/dist/utils/lockedJsonFile.js +224 -0
- package/dist/utils/platform.d.ts +11 -0
- package/dist/utils/platform.js +26 -0
- package/dist/utils/stableJson.d.ts +2 -0
- package/dist/utils/stableJson.js +12 -0
- package/dist/version.d.ts +3 -2
- package/dist/version.js +3 -2
- package/dist/watcherStatus.d.ts +2 -2
- package/dist/watcherStatus.js +18 -0
- package/docs/CODE_WIKI.md +1648 -0
- package/docs/agentseal-integration.md +150 -0
- package/docs/architecture.md +23 -0
- package/docs/assets/patchwarden-desktop-onboarding.png +0 -0
- package/docs/assets/patchwarden-oss-demo.gif +0 -0
- package/docs/control-center/README.md +4 -4
- package/docs/dashboard-overview.md +86 -0
- package/docs/demo.md +8 -0
- package/docs/desktop-app.md +175 -0
- package/docs/direct-session-workflow.md +98 -0
- package/docs/evidence-pack-schema.md +215 -0
- package/docs/lineage-evidence-pack-workflow.md +127 -0
- package/docs/mcp-inspector-testing.md +200 -0
- package/docs/open-source-application.md +168 -0
- package/docs/opencode-worker.md +151 -0
- package/docs/openhands-worker.md +181 -0
- package/docs/release-checklist.md +1 -1
- package/docs/release-evidence.md +82 -0
- package/docs/spec-kit-integration.md +131 -0
- package/docs/task-safe-review-workflow.md +98 -0
- package/docs/threat-model.md +97 -0
- package/docs/user-feedback.md +40 -0
- package/docs/why-patchwarden.md +110 -0
- package/examples/config.example.json +4 -2
- package/package.json +28 -11
- 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 +447 -6
- package/scripts/checks/control-smoke.js +19 -8
- package/scripts/checks/http-mcp-smoke.js +82 -4
- package/scripts/checks/lifecycle-smoke.js +39 -33
- package/scripts/checks/mcp-manifest-check.js +1 -1
- package/scripts/checks/mcp-smoke.js +30 -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 +9 -0
- package/scripts/control/get-patchwarden-health.ps1 +1 -1
- package/scripts/control/manage-patchwarden.ps1 +69 -4
- package/scripts/control/provision-patchwarden-tunnel.ps1 +127 -0
- package/scripts/control/run-background-supervisor.ps1 +41 -0
- package/scripts/control/start-patchwarden-tunnel.ps1 +126 -32
- package/scripts/generate-demo-gif.py +320 -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 +29 -15
- package/src/assessments/assessmentStore.ts +55 -43
- package/src/assessments/confirmCli.ts +5 -4
- package/src/config.ts +105 -11
- package/src/control/fileManager.ts +40 -0
- package/src/control/middleware/auth.ts +35 -0
- package/src/control/middleware/static.ts +84 -0
- package/src/control/routeTable.ts +301 -0
- package/src/control/routes/audit.ts +329 -0
- package/src/control/routes/evidence.ts +107 -0
- package/src/control/routes/lineage.ts +92 -0
- package/src/control/routes/policy.ts +81 -0
- package/src/control/routes/process.ts +505 -0
- package/src/control/routes/sessions.ts +314 -0
- package/src/control/routes/status.ts +405 -0
- package/src/control/routes/taskActions.ts +214 -0
- package/src/control/routes/tasks.ts +331 -0
- package/src/control/routes/workspace.ts +234 -0
- package/src/control/runtime.ts +508 -0
- package/src/control/server.ts +165 -0
- package/src/control/shared.ts +360 -0
- package/src/controlCenter.ts +7 -2347
- package/src/direct/directAudit.ts +17 -9
- package/src/direct/directGuards.ts +100 -51
- package/src/direct/directPatch.ts +95 -8
- package/src/direct/directSessionStore.ts +211 -55
- package/src/direct/directVerification.ts +9 -23
- package/src/doctor.ts +766 -492
- package/src/goal/acceptanceEngine.ts +1 -1
- package/src/goal/goalProgress.ts +17 -13
- package/src/goal/goalReport.ts +263 -0
- package/src/goal/goalStatus.ts +25 -3
- package/src/goal/goalStore.ts +241 -54
- package/src/goal/handoffExport.ts +6 -6
- package/src/goal/specKitImport.ts +355 -0
- package/src/goal/subgoalSync.ts +15 -10
- package/src/goal/worktreeManager.ts +139 -22
- package/src/httpServer.ts +130 -44
- package/src/index.ts +7 -4
- package/src/logging.ts +20 -4
- 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 +342 -80
- package/src/runner/cli.ts +7 -6
- package/src/runner/postTaskCleanup.ts +55 -7
- package/src/runner/processSecurity.ts +433 -0
- package/src/runner/runTask.ts +441 -292
- package/src/runner/simpleProcess.ts +68 -33
- package/src/{taskProgress.ts → runner/taskProgress.ts} +3 -3
- package/src/{taskRuntime.ts → runner/taskRuntime.ts} +4 -3
- package/src/runner/taskStatusStore.ts +54 -0
- package/src/runner/watch.ts +290 -57
- package/src/security/contentRedaction.ts +31 -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 +276 -273
- 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} +29 -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} +196 -89
- package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +11 -11
- 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} +29 -19
- package/src/tools/{schemaDriftCheck.ts → diagnostics/schemaDriftCheck.ts} +3 -3
- package/src/tools/{createDirectSession.ts → direct/createDirectSession.ts} +9 -9
- package/src/tools/{finalizeDirectSession.ts → direct/finalizeDirectSession.ts} +11 -9
- 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 +379 -0
- package/src/tools/dispatch/diagnosticDispatch.ts +106 -0
- package/src/tools/dispatch/directDispatch.ts +168 -0
- package/src/tools/dispatch/goalDispatch.ts +128 -0
- package/src/tools/dispatch/releaseDispatch.ts +66 -0
- package/src/tools/dispatch/types.ts +24 -0
- 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} +43 -28
- package/src/tools/goals/index.ts +3 -0
- package/src/tools/{savePlan.ts → goals/savePlan.ts} +38 -8
- package/src/tools/registry.ts +54 -1858
- package/src/tools/{checkReleaseGate.ts → release/checkReleaseGate.ts} +3 -3
- package/src/tools/release/index.ts +2 -0
- package/src/tools/{releaseMode.ts → release/releaseMode.ts} +42 -24
- package/src/tools/tasks/cancelTask.ts +109 -0
- package/src/tools/{createTask.ts → tasks/createTask.ts} +56 -33
- package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +8 -8
- package/src/tools/tasks/evidencePack.ts +504 -0
- package/src/tools/{getTaskFile.ts → tasks/getTaskFile.ts} +4 -4
- package/src/tools/{getTaskStatus.ts → tasks/getTaskStatus.ts} +7 -5
- package/src/tools/{getTaskStdoutTail.ts → tasks/getTaskStdoutTail.ts} +13 -11
- package/src/tools/{getTaskSummary.ts → tasks/getTaskSummary.ts} +44 -33
- package/src/tools/tasks/index.ts +18 -0
- package/src/tools/{listTasks.ts → tasks/listTasks.ts} +4 -4
- package/src/tools/{reconcileTasks.ts → tasks/reconcileTasks.ts} +68 -66
- package/src/tools/tasks/retryTask.ts +69 -0
- package/src/tools/{runTaskLoop.ts → tasks/runTaskLoop.ts} +70 -35
- package/src/tools/{runVerification.ts → tasks/runVerification.ts} +6 -3
- package/src/tools/{taskLineage.ts → tasks/taskLineage.ts} +8 -7
- package/src/tools/{taskOutputs.ts → tasks/taskOutputs.ts} +27 -20
- package/src/tools/{androidDoctor.ts → workspace/androidDoctor.ts} +15 -10
- package/src/tools/{applyPatch.ts → workspace/applyPatch.ts} +38 -16
- package/src/tools/{discardWorktree.ts → workspace/discardWorktree.ts} +3 -3
- package/src/tools/workspace/index.ts +10 -0
- package/src/tools/{listAgents.ts → workspace/listAgents.ts} +11 -4
- package/src/tools/{listWorkspace.ts → workspace/listWorkspace.ts} +3 -3
- package/src/tools/{mergeWorktree.ts → workspace/mergeWorktree.ts} +3 -3
- package/src/tools/{readWorkspaceFile.ts → workspace/readWorkspaceFile.ts} +8 -8
- package/src/tools/{recommendAgentForTask.ts → workspace/recommendAgentForTask.ts} +3 -3
- package/src/tools/{searchWorkspace.ts → workspace/searchWorkspace.ts} +5 -5
- package/src/tools/workspace/syncFile.ts +292 -0
- package/src/utils/atomicFile.ts +58 -0
- package/src/utils/boundedFile.ts +116 -0
- package/src/utils/lockedJsonFile.ts +251 -0
- package/src/utils/platform.ts +31 -0
- package/src/utils/stableJson.ts +12 -0
- package/src/version.ts +3 -2
- package/src/watcherStatus.ts +19 -1
- package/ui/desktop-bootstrap.js +11 -0
- package/ui/desktop-bridge.js +36 -0
- package/ui/desktop.css +160 -0
- package/ui/getting-started.js +93 -0
- package/ui/i18n.js +484 -0
- package/ui/log-parser.js +30 -0
- package/ui/pages/audit.html +204 -3
- package/ui/pages/dashboard.html +937 -79
- package/ui/pages/direct-sessions.html +609 -60
- package/ui/pages/getting-started.html +43 -0
- package/ui/pages/logs.html +105 -22
- package/ui/pages/settings.html +101 -0
- package/ui/pages/task-detail.html +468 -438
- package/ui/pages/tasks.html +648 -65
- package/ui/pages/workspace.html +38 -1
- package/ui/settings.js +245 -0
- package/PatchWarden-Control.cmd +0 -6
- package/Restart-PatchWarden-Control.cmd +0 -6
- package/dist/test/unit/acceptance-engine.test.d.ts +0 -1
- package/dist/test/unit/acceptance-engine.test.js +0 -228
- package/dist/test/unit/agent-router.test.d.ts +0 -1
- package/dist/test/unit/agent-router.test.js +0 -287
- package/dist/test/unit/android-doctor.test.d.ts +0 -1
- package/dist/test/unit/android-doctor.test.js +0 -118
- package/dist/test/unit/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/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 -213
- 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 -130
- 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-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 -190
- 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 -109
- 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/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/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/evidencePack.js +0 -168
- package/dist/tools/finalizeDirectSession.js +0 -84
- package/dist/tools/goalSubgoalTask.js +0 -110
- package/dist/tools/killTask.d.ts +0 -15
- package/dist/tools/retryTask.js +0 -45
- package/dist/tools/runVerification.js +0 -36
- package/dist/tools/syncFile.js +0 -65
- package/docs/control-center/control-center-daily-driver.md +0 -211
- package/docs/control-center/control-center-mvp.md +0 -205
- package/docs/control-center/control-center-phase2.md +0 -159
- package/docs/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/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/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/diagnose-task.test.ts +0 -544
- package/src/test/unit/direct-guards.test.ts +0 -297
- package/src/test/unit/discovery-token-store.test.ts +0 -181
- package/src/test/unit/evidence-pack.test.ts +0 -142
- 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-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 -243
- 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 -150
- 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/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/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/evidencePack.ts +0 -205
- 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/{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/{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/{runVerification.d.ts → tasks/runVerification.d.ts} +0 -0
- /package/dist/tools/{taskLineage.d.ts → tasks/taskLineage.d.ts} +0 -0
- /package/dist/tools/{taskOutputs.d.ts → tasks/taskOutputs.d.ts} +0 -0
- /package/dist/tools/{waitForTask.d.ts → tasks/waitForTask.d.ts} +0 -0
- /package/dist/tools/{waitForTask.js → tasks/waitForTask.js} +0 -0
- /package/dist/tools/{androidDoctor.d.ts → workspace/androidDoctor.d.ts} +0 -0
- /package/dist/tools/{discardWorktree.d.ts → workspace/discardWorktree.d.ts} +0 -0
- /package/dist/tools/{listWorkspace.d.ts → workspace/listWorkspace.d.ts} +0 -0
- /package/dist/tools/{mergeWorktree.d.ts → workspace/mergeWorktree.d.ts} +0 -0
- /package/dist/tools/{readWorkspaceFile.d.ts → workspace/readWorkspaceFile.d.ts} +0 -0
- /package/dist/tools/{recommendAgentForTask.d.ts → workspace/recommendAgentForTask.d.ts} +0 -0
- /package/dist/tools/{searchWorkspace.d.ts → workspace/searchWorkspace.d.ts} +0 -0
- /package/src/tools/{toolSearch.ts → catalog/toolSearch.ts} +0 -0
- /package/src/tools/{toolUsageStats.ts → catalog/toolUsageStats.ts} +0 -0
- /package/src/tools/{getTaskProgress.ts → tasks/getTaskProgress.ts} +0 -0
- /package/src/tools/{killTask.ts → tasks/killTask.ts} +0 -0
- /package/src/tools/{waitForTask.ts → tasks/waitForTask.ts} +0 -0
package/README.md
CHANGED
|
@@ -8,20 +8,27 @@
|
|
|
8
8
|
[](https://nodejs.org/)
|
|
9
9
|
[](LICENSE)
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
[CHANGELOG](CHANGELOG.md)、[迁移指南](docs/migration-from-safe-bifrost.md)和
|
|
13
|
-
[发布检查清单](docs/release-checklist.md)。GitHub Release / npm 发布状态需要在发布前单独核对。
|
|
11
|
+
**让 ChatGPT、Codex 等 MCP 客户端负责规划与验收,让本地编程 Agent 在明确的安全边界内执行,并留下可审计证据。**
|
|
14
12
|
|
|
15
13
|
PatchWarden 是一个面向本地编程 Agent 的安全 MCP 桥接器。上游的
|
|
16
14
|
ChatGPT、Codex、OpenCode 或其他 MCP 客户端负责规划与验收,
|
|
17
15
|
PatchWarden 负责把计划保存成工作区内任务,再由预先配置的本地 Agent
|
|
18
16
|
执行,并返回结果、代码差异和独立测试记录。
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
[下载 Windows 安装版 v1.6.0](https://github.com/jiezeng2004-design/PatchWarden/releases/download/v1.6.0/PatchWarden-Setup-1.6.0-x64.exe)
|
|
19
|
+
· [免安装 ZIP](https://github.com/jiezeng2004-design/PatchWarden/releases/download/v1.6.0/PatchWarden-Portable-1.6.0-x64.zip)
|
|
20
|
+
· [校验文件](https://github.com/jiezeng2004-design/PatchWarden/releases/download/v1.6.0/PatchWarden-Desktop-SHA256SUMS.txt)
|
|
21
|
+
· [三分钟快速开始](#三分钟快速开始)
|
|
22
|
+
· [Discussions](https://github.com/jiezeng2004-design/PatchWarden/discussions)
|
|
23
|
+
|
|
24
|
+

|
|
21
25
|
|
|
22
|
-
>
|
|
26
|
+
<sub>PatchWarden Desktop 真实首启界面,来自隐私安全的桌面 smoke 验收;截图未使用真实工作区、账号或凭据。</sub>
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
当前源码版本:**v1.6.1**;当前 Windows Release:**v1.6.0**;npm `latest`:**v1.5.1**。
|
|
29
|
+
Windows 首次体验推荐上面的安装版;npm/CLI 用户请固定已发布版本。查看
|
|
30
|
+
[CHANGELOG](CHANGELOG.md)、[迁移指南](docs/migration-from-safe-bifrost.md)和
|
|
31
|
+
[发布检查清单](docs/release-checklist.md)。
|
|
25
32
|
|
|
26
33
|
> [!IMPORTANT]
|
|
27
34
|
> PatchWarden 不是通用远程 Shell。MCP 客户端不能随意执行命令:
|
|
@@ -33,7 +40,7 @@ PatchWarden 负责把计划保存成工作区内任务,再由预先配置的
|
|
|
33
40
|
- [它解决什么问题](#它解决什么问题)
|
|
34
41
|
- [运行结构](#运行结构)
|
|
35
42
|
- [环境要求](#环境要求)
|
|
36
|
-
- [
|
|
43
|
+
- [三分钟快速开始](#三分钟快速开始)
|
|
37
44
|
- [完整配置说明](#完整配置说明)
|
|
38
45
|
- [接入 OpenCode](#接入-opencode)
|
|
39
46
|
- [接入 Codex](#接入-codex)
|
|
@@ -44,6 +51,7 @@ PatchWarden 负责把计划保存成工作区内任务,再由预先配置的
|
|
|
44
51
|
- [诊断与健康检查](#诊断与健康检查)
|
|
45
52
|
- [踩坑记录与故障排查](#踩坑记录与故障排查)
|
|
46
53
|
- [Direct 模式:ChatGPT 直接开发](#direct-模式chatgpt-直接开发)
|
|
54
|
+
- [生态适配](#生态适配)
|
|
47
55
|
- [安全边界与本地数据](#安全边界与本地数据)
|
|
48
56
|
- [升级与旧版本迁移](#升级与旧版本迁移)
|
|
49
57
|
- [开发与发布验证](#开发与发布验证)
|
|
@@ -57,9 +65,9 @@ PatchWarden 负责把计划保存成工作区内任务,再由预先配置的
|
|
|
57
65
|
- 每个任务必须指定位于 `workspaceRoot` 内的 `repo_path`。
|
|
58
66
|
- Agent 启动命令来自本地配置,而不是来自模型输入。
|
|
59
67
|
- 测试命令必须精确匹配 `allowedTestCommands`。
|
|
60
|
-
-
|
|
68
|
+
- 任务完成后保存结构化结果、有界且脱敏的差异、文件统计和独立验证记录。
|
|
61
69
|
- 工作区外出现变化时,任务会标记为作用域违规,而不是悄悄接受。
|
|
62
|
-
- `.env`、Token、SSH 密钥、Cookie
|
|
70
|
+
- `.patchwarden/` 不是敏感路径豁免区;`.env`、Token、SSH 密钥、Cookie、凭据文件等敏感名称无论位于哪一层目录都不可读。
|
|
63
71
|
|
|
64
72
|
适合的场景:
|
|
65
73
|
|
|
@@ -133,9 +141,33 @@ where.exe codex
|
|
|
133
141
|
应用程序,它不一定是可供 PatchWarden 调用的 Codex CLI。请安装或指定真正的
|
|
134
142
|
CLI,或者先把 OpenCode 配置为执行 Agent。
|
|
135
143
|
|
|
136
|
-
##
|
|
144
|
+
## 三分钟快速开始
|
|
145
|
+
|
|
146
|
+
如果已经安装 Node.js 18+ 和至少一个本地编程 Agent,这条 Windows 路径的目标是
|
|
147
|
+
在三分钟内完成首次只读健康检查。首次体验不需要配置 ChatGPT Tunnel。
|
|
148
|
+
|
|
149
|
+
### 方案 A:Windows 安装版(首次体验推荐)
|
|
150
|
+
|
|
151
|
+
1. 下载 [Windows 安装版 v1.6.0](https://github.com/jiezeng2004-design/PatchWarden/releases/download/v1.6.0/PatchWarden-Setup-1.6.0-x64.exe)
|
|
152
|
+
和 [SHA256 校验文件](https://github.com/jiezeng2004-design/PatchWarden/releases/download/v1.6.0/PatchWarden-Desktop-SHA256SUMS.txt)。
|
|
153
|
+
2. 在 PowerShell 中校验安装包:
|
|
154
|
+
|
|
155
|
+
```powershell
|
|
156
|
+
Get-FileHash .\PatchWarden-Setup-1.6.0-x64.exe -Algorithm SHA256
|
|
157
|
+
```
|
|
137
158
|
|
|
138
|
-
|
|
159
|
+
当前发布值应为 `77d655545925ae283a0caed0fff12554debac15b54c5decf1ae8f3867703058e`;
|
|
160
|
+
仍应以同一 Release 的校验文件为准。安装包尚未代码签名,Windows SmartScreen
|
|
161
|
+
可能显示“未知发布者”。
|
|
162
|
+
|
|
163
|
+
3. 安装并打开 PatchWarden Desktop,选择一个只包含项目的专用工作区。
|
|
164
|
+
4. 首屏选择 **本地 MCP**,确认检测到的 Agent,让只读健康检查完成。
|
|
165
|
+
|
|
166
|
+
成功标志:向导能够进入只读控制台,显示所选工作区,并完成 Agent 与运行环境检查。
|
|
167
|
+
安装成功或遇到阻塞时,请到 [Discussions](https://github.com/jiezeng2004-design/PatchWarden/discussions)
|
|
168
|
+
记录操作系统、Node.js 版本、使用的 Agent 和卡住的步骤。
|
|
169
|
+
|
|
170
|
+
### 方案 B:从源码运行(开发者)
|
|
139
171
|
|
|
140
172
|
源码方式最适合完整使用 Watcher、Windows 一键启动器和诊断脚本。
|
|
141
173
|
|
|
@@ -170,7 +202,7 @@ npm.cmd run watch
|
|
|
170
202
|
|
|
171
203
|
保持该窗口运行,再按照后文配置 OpenCode、Codex 或 ChatGPT Connector。
|
|
172
204
|
|
|
173
|
-
### 方案
|
|
205
|
+
### 方案 C:使用 npm 包
|
|
174
206
|
|
|
175
207
|
npm 包适合让 MCP 客户端通过固定版本启动 PatchWarden。为了执行任务,
|
|
176
208
|
仍然必须另外启动 Watcher。
|
|
@@ -212,11 +244,13 @@ Copy-Item .\examples\config.example.json .\patchwarden.config.json
|
|
|
212
244
|
"agents": {
|
|
213
245
|
"opencode": {
|
|
214
246
|
"command": "opencode",
|
|
215
|
-
"args": ["run", "{prompt}"]
|
|
247
|
+
"args": ["run", "{prompt}"],
|
|
248
|
+
"envAllowlist": []
|
|
216
249
|
},
|
|
217
250
|
"codex": {
|
|
218
251
|
"command": "codex",
|
|
219
|
-
"args": ["exec", "--cd", "{repo}", "{prompt}"]
|
|
252
|
+
"args": ["exec", "--cd", "{repo}", "{prompt}"],
|
|
253
|
+
"envAllowlist": []
|
|
220
254
|
}
|
|
221
255
|
},
|
|
222
256
|
"allowedTestCommands": [
|
|
@@ -245,6 +279,7 @@ Copy-Item .\examples\config.example.json .\patchwarden.config.json
|
|
|
245
279
|
| `tasksDir` | 是 | 任务和结果目录,通常使用 `.patchwarden/tasks`。 |
|
|
246
280
|
| `toolProfile` | 否 | `full`、`chatgpt_core`、`chatgpt_direct` 或 `chatgpt_search`;本地客户端推荐 `full`,动态工具发现场景使用 `chatgpt_search`。 |
|
|
247
281
|
| `agents` | 是 | 可执行 Agent 白名单;支持 `{repo}` 和 `{prompt}` 占位符。 |
|
|
282
|
+
| `agents.<name>.envAllowlist` | 否 | 显式传给该 Agent 的 provider 环境变量名;默认不继承。Tunnel owner token 禁止透传。 |
|
|
248
283
|
| `allowedTestCommands` | 是 | 独立验证命令白名单,调用时必须精确匹配。 |
|
|
249
284
|
| `repoAllowedTestCommands` | 否 | 按工作区相对仓库路径增加精确验证命令;不支持通配符。 |
|
|
250
285
|
| `maxReadFileBytes` | 是 | MCP 单次文件读取上限。 |
|
|
@@ -368,6 +403,21 @@ PATCHWARDEN_TOOL_PROFILE = "full"
|
|
|
368
403
|
|
|
369
404
|
## 接入 ChatGPT Connector
|
|
370
405
|
|
|
406
|
+
> 当前 ChatGPT 术语为 **developer-mode app / Plugins**;旧版文档与部分界面曾称为 Connector。新配置请以 Plugins 为准。
|
|
407
|
+
|
|
408
|
+
### 桌面版八步新手流程
|
|
409
|
+
|
|
410
|
+
1. 确认 OpenAI Platform 账号具有 Tunnel 权限,并在目标 ChatGPT workspace 开启 developer mode。
|
|
411
|
+
2. 打开 [Platform Tunnel 设置](https://platform.openai.com/settings/organization/tunnels),创建 Tunnel,并关联目标 ChatGPT workspace。
|
|
412
|
+
3. 启动 PatchWarden Desktop,在“设置 → MCP 与隧道”检测或选择现有的 `tunnel-client.exe`;应用不会自动下载或执行新软件。
|
|
413
|
+
4. 输入 Core Tunnel ID 与专用 Tunnel runtime API key。该凭据对应 `CONTROL_PLANE_API_KEY`,**不是**普通应用使用的 `OPENAI_API_KEY`。
|
|
414
|
+
5. 配置环境代理、无代理或不含凭据的 HTTP/HTTPS/SOCKS5(Mixed)代理,并先确认代理端口可达。
|
|
415
|
+
6. 点击“配置并验证 Core”。桌面端初始化 `patchwarden` profile,运行 `tunnel-client doctor --explain --json`,验证成功后才用 Windows DPAPI 保存凭据;随后启动 Core。
|
|
416
|
+
7. 在“开始使用”确认 Tunnel ready、Watcher healthy,并确认 `chatgpt_core` 发现固定的 26 个工具。Direct 是设置页中的可选高级能力,未启用时“全部启动”只启动 Core。
|
|
417
|
+
8. 在 ChatGPT **Settings → Plugins** 创建 developer-mode app,选择刚创建的 Tunnel;重新连接后新建对话,调用 `health_check` 验证。
|
|
418
|
+
|
|
419
|
+
首次引导也可选择“本地 MCP”。该路线只配置安全工作区和本地 MCP 客户端,可以跳过第 1、2、4、5、7、8 步中的 Platform/ChatGPT Tunnel 操作。
|
|
420
|
+
|
|
371
421
|
推荐的 Windows 链路:
|
|
372
422
|
|
|
373
423
|
```text
|
|
@@ -571,19 +621,25 @@ ChatGPT 任务应优先选择前三个守护模板:只读诊断使用 `inspect
|
|
|
571
621
|
| `progress.md` | Agent 写入的进度记录。 |
|
|
572
622
|
| `result.md` | 人类可读的执行报告。 |
|
|
573
623
|
| `result.json` | 结构化结果、路径、变更、警告和后续建议。 |
|
|
574
|
-
| `diff.patch` |
|
|
624
|
+
| `diff.patch` | 最多 20 MiB 的任务差异证据;疑似凭据会在落盘前脱敏,超限会明确标记截断。 |
|
|
575
625
|
| `artifact_manifest.json` | 构建或发布产物的路径、类型、大小与 SHA-256。 |
|
|
576
626
|
| `file-stats.json` | 文件级增删统计。 |
|
|
577
627
|
| `verify.json` | 每条独立验证命令的结构化记录。 |
|
|
578
628
|
| `verify.log` | 独立验证的可读日志。 |
|
|
579
629
|
| `test.log` | Agent 执行过程中产生的测试输出。 |
|
|
580
630
|
|
|
631
|
+
面向客户端的任务产物读取受 `maxReadFileBytes` 限制;diff、summary 与日志 tail
|
|
632
|
+
只读取有界前缀或尾部。`audit_task` 最多扫描 200 个 Markdown 文件和 4 MiB 文档内容,
|
|
633
|
+
达到预算会返回 warning。invocation/reconcile 等持续日志使用跨进程锁内的有界追加,
|
|
634
|
+
超限时保留最近内容并写入明确的截断标记。
|
|
635
|
+
|
|
581
636
|
本地 Agent 声称“已推送”“已发布”不属于可靠的远程证据。GitHub、npm、
|
|
582
637
|
Tag 和 Release 必须再用对应平台的实时状态核验。
|
|
583
638
|
|
|
584
639
|
## HTTP MCP 模式
|
|
585
640
|
|
|
586
|
-
HTTP Server 只绑定 `127.0.0.1`,默认端口 7331
|
|
641
|
+
HTTP Server 只绑定 `127.0.0.1`,默认端口 7331,不会直接监听局域网;
|
|
642
|
+
HTTP MCP 与 Control Center 同时拒绝非 `127.0.0.1`/`localhost` 的 Host,防止 DNS rebinding 绕过回环边界。
|
|
587
643
|
|
|
588
644
|
终端 1,启动 Watcher:
|
|
589
645
|
|
|
@@ -658,10 +714,43 @@ Direct 两种模式的启动、停止、重启与状态。也可以在 PowerShel
|
|
|
658
714
|
.\PatchWarden.cmd restart all
|
|
659
715
|
.\PatchWarden.cmd status all
|
|
660
716
|
.\PatchWarden.cmd kill all
|
|
661
|
-
.\Stop-PatchWarden.cmd
|
|
717
|
+
.\scripts\launchers\Stop-PatchWarden.cmd
|
|
662
718
|
```
|
|
663
719
|
|
|
664
|
-
日常桌面入口:`PatchWarden-Desktop.cmd` 启动托盘并确保 Control Center 可用,不自动打开额外浏览器窗口。只有调试托盘时才使用 `PatchWarden-Control-Tray.cmd --foreground`;需要完整 Web 控制台时打开 `PatchWarden-Control.cmd`;需要一键收尾时使用 `Stop-PatchWarden.cmd` 关闭 Core/Direct、Control Center 和托盘。
|
|
720
|
+
日常桌面入口:`scripts\launchers\PatchWarden-Desktop.cmd` 启动托盘并确保 Control Center 可用,不自动打开额外浏览器窗口。只有调试托盘时才使用 `scripts\launchers\PatchWarden-Control-Tray.cmd --foreground`;需要完整 Web 控制台时打开 `scripts\launchers\PatchWarden-Control.cmd`;需要一键收尾时使用 `scripts\launchers\Stop-PatchWarden.cmd` 关闭 Core/Direct、Control Center 和托盘。
|
|
721
|
+
|
|
722
|
+
### Windows 安装版
|
|
723
|
+
|
|
724
|
+
公开分发可以构建 PatchWarden Desktop 安装包和免安装 ZIP。它提供 Electron
|
|
725
|
+
独立窗口、首次工作区引导、系统托盘和设置页,同时继续复用相同的
|
|
726
|
+
loopback Control Center 与安全控制 API。桌面依赖隔离在私有的
|
|
727
|
+
`desktop/` 子包中,不进入 `patchwarden` npm 包。
|
|
728
|
+
|
|
729
|
+
安装版仍要求 Windows x64、Node.js 18+ 和 tunnel-client。首次启动会在
|
|
730
|
+
`PATH`、当前用户目录和工作区附近做有界检测;未找到时可进入只读控制台,
|
|
731
|
+
再从“设置 -> MCP 与隧道”选择 `tunnel-client.exe` 并查看 SHA256 校验步骤。
|
|
732
|
+
应用不会自动下载或运行新软件。设置页还提供 Direct 显式开关,以及环境代理、
|
|
733
|
+
无代理、手动代理三种模式;手动代理只接受不含凭据的 http/https/socks5 URL。
|
|
734
|
+
启动和重启只有在 health/ready 与 Core Watcher 验证通过后才会报告成功。
|
|
735
|
+
桌面端支持 Codex、OpenCode、Claude Code、Gemini CLI、GitHub Copilot CLI、
|
|
736
|
+
Qwen Code、Kimi Code 和 Aider;只读取各 Agent 配置中的模型字段,不读取凭据。
|
|
737
|
+
|
|
738
|
+
```powershell
|
|
739
|
+
npm.cmd install --prefix desktop --cache .\.npm-cache
|
|
740
|
+
npm.cmd run desktop:test
|
|
741
|
+
npm.cmd run desktop:preflight
|
|
742
|
+
npm.cmd run desktop:package
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
安装包、免安装 ZIP 与 SHA256 文件输出到 `release\desktop`。首版不包含自动更新或
|
|
746
|
+
代码签名,Windows SmartScreen 可能提示未知发布者;发布前必须核对
|
|
747
|
+
GitHub Release 来源和 SHA256。完整安装、运行与卸载说明见
|
|
748
|
+
[docs/desktop-app.md](docs/desktop-app.md)。
|
|
749
|
+
|
|
750
|
+
`desktop:preflight` 会使用唯一目录完成 clean build、完整单测、桌面测试、npm 包面、
|
|
751
|
+
Electron 目录包、仓库外 26-tool manifest 与隔离 unpacked UI/单实例验收,并生成
|
|
752
|
+
`preflight-report.json` / `preflight-report.md`。正式发布前必须在干净 checkout 运行
|
|
753
|
+
`npm.cmd run desktop:preflight:release`;该命令发现未提交路径时会直接阻断。
|
|
665
754
|
|
|
666
755
|
旧的单用途入口保留在 `scripts/launchers/` 作为兼容层;个人入口位于
|
|
667
756
|
`.local/launchers/`,并继续被 Git 和发布包排除。`stop` / `restart` 会同时检查
|
|
@@ -751,6 +840,31 @@ powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%CD%\scripts\control\st
|
|
|
751
840
|
`chatgpt_core` / `patchwarden` → `127.0.0.1:8080`,
|
|
752
841
|
`chatgpt_direct` / `patchwarden-direct` → `127.0.0.1:8081`。
|
|
753
842
|
|
|
843
|
+
## Dashboard 主要页面与推荐工作流
|
|
844
|
+
|
|
845
|
+
PatchWarden Dashboard (Control Center) 提供以下主要页面:
|
|
846
|
+
|
|
847
|
+
- **控制台 (Dashboard)**: 系统总览,包含 Repo selector、Health Score、服务状态、Release 卡片、Project Policy、Lineage、Evidence Pack、Stale 任务提示、最近任务列表、系统状态(含 Copy diagnostics)。
|
|
848
|
+
- **任务面板 (Tasks)**: 任务列表,支持按 repo_path / status / acceptance_status / warning type / agent / date range 过滤,每行提供 safe_result / safe_audit / safe_test_summary / safe_diff_summary 快捷操作。
|
|
849
|
+
- **任务详情 (Task Detail)**: 默认展示 safe 摘要(safe_result / safe_test_summary / safe_diff_summary / safe_audit),配置上限内的 result / diff / test log 在折叠的高级区按需加载。
|
|
850
|
+
- **Direct 会话 (Direct Sessions)**: 按 active / finalized / audited / expired 分组,提供 safe_direct_summary / safe_finalize_direct_session / safe_audit_direct_session 快捷操作。
|
|
851
|
+
- **审计日志 / Warnings (Audit)**: 按 warning 类型聚合,显示 affected tasks / severity / recommended action。
|
|
852
|
+
- **工作区 (Workspace)**: workspace 一级目录与项目列表。
|
|
853
|
+
- **日志 (Logs)**: Core / Direct / Watcher / Control Center 日志尾部。
|
|
854
|
+
|
|
855
|
+
推荐工作流:
|
|
856
|
+
|
|
857
|
+
1. 在 Dashboard 顶部选择目标 repo。
|
|
858
|
+
2. 查看 Health Score 确认系统健康。
|
|
859
|
+
3. 在最近任务列表点击 safe_result 快捷查看任务摘要。
|
|
860
|
+
4. 打开 Task Detail 进行 safe-first 验收,仅在需要时展开高级区查看有界日志。
|
|
861
|
+
5. 使用 Lineage Detail 查看 run_task_loop 的成功/失败原因。
|
|
862
|
+
6. 验收完成后导出 Evidence Pack。
|
|
863
|
+
7. Direct 会话用于独立验证,完成后 finalize + audit。
|
|
864
|
+
8. 遇到问题时点击 Copy diagnostics 复制诊断信息发给 ChatGPT / Codex / opencode 排查。
|
|
865
|
+
|
|
866
|
+
> 详细说明见 [docs/dashboard-overview.md](docs/dashboard-overview.md)、[docs/task-safe-review-workflow.md](docs/task-safe-review-workflow.md)、[docs/lineage-evidence-pack-workflow.md](docs/lineage-evidence-pack-workflow.md)、[docs/direct-session-workflow.md](docs/direct-session-workflow.md)。
|
|
867
|
+
|
|
754
868
|
## 踩坑记录与故障排查
|
|
755
869
|
|
|
756
870
|
### 快速对照表
|
|
@@ -868,6 +982,12 @@ creation. `export_task_evidence_pack` writes bounded `evidence.json` and
|
|
|
868
982
|
`EVIDENCE.md` files under `.patchwarden/evidence-packs/<lineage_id>/` without
|
|
869
983
|
stdout/stderr tails, full diffs, verification logs, or sensitive file content.
|
|
870
984
|
|
|
985
|
+
Goal Session 的状态变更通过共享的跨进程 mutation lock 串行化并原子落盘;
|
|
986
|
+
非空 Goal 的所有子目标都被 `accept_subgoal` 接受后,Goal 会自动转为 `completed`。
|
|
987
|
+
`create_subgoal_task` 以 Goal 已保存的 `repo_path` 为权威,调用方传入其他仓库会以
|
|
988
|
+
`goal_repo_mismatch` 拒绝;隔离 worktree 也从该仓库创建,且 create/merge/discard
|
|
989
|
+
共用仓库级 lifecycle lock。
|
|
990
|
+
|
|
871
991
|
`get_project_policy` 是 v1.3 的只读策略入口,返回 `.patchwarden/project-policy.json`
|
|
872
992
|
的有界 effective policy 与 release readiness,不会扩大命令白名单。v1.3 的
|
|
873
993
|
`release_check`、`release_prepare`、`release_verify`、`release_cleanup` 仅在 `full`
|
|
@@ -971,12 +1091,26 @@ Direct 模式在受控 session 内执行,具有以下硬性限制:
|
|
|
971
1091
|
- 禁止修改 `node_modules`
|
|
972
1092
|
- 禁止手动修改 release/dist
|
|
973
1093
|
- 禁止修改二进制文件
|
|
1094
|
+
- 只修改大小受限的 UTF-8 文本;补丁按 UTF-8 字节计数
|
|
1095
|
+
- 补丁或同步结果包含疑似凭据时拒绝写入
|
|
1096
|
+
- 同一 session 的 patch、sync、verify、finalize、audit 串行化
|
|
974
1097
|
- 所有操作限制在 `workspaceRoot` 和 session repo 内
|
|
975
1098
|
|
|
976
1099
|
### 不支持内容
|
|
977
1100
|
|
|
978
1101
|
Direct 模式不支持:shell、delete、rename、commit、push、publish、deployment。
|
|
979
1102
|
|
|
1103
|
+
## 生态适配
|
|
1104
|
+
|
|
1105
|
+
- [为什么需要 PatchWarden](docs/why-patchwarden.md)
|
|
1106
|
+
- [Evidence Pack v2 文件结构](docs/evidence-pack-schema.md)
|
|
1107
|
+
- [Spec Kit 集成模式](docs/spec-kit-integration.md)
|
|
1108
|
+
- [AgentSeal 集成模式](docs/agentseal-integration.md)
|
|
1109
|
+
- [MCP Inspector 测试](docs/mcp-inspector-testing.md)
|
|
1110
|
+
- [OpenCode worker 集成](docs/opencode-worker.md)
|
|
1111
|
+
- [OpenHands worker 集成](docs/openhands-worker.md)
|
|
1112
|
+
- [威胁模型](docs/threat-model.md)
|
|
1113
|
+
|
|
980
1114
|
## 安全边界与本地数据
|
|
981
1115
|
|
|
982
1116
|
PatchWarden 的主要保护:
|
|
@@ -985,9 +1119,9 @@ PatchWarden 的主要保护:
|
|
|
985
1119
|
- Agent 命令和参数模板必须预先配置。
|
|
986
1120
|
- 验证命令必须精确匹配白名单。
|
|
987
1121
|
- 文件访问被限制在 `workspaceRoot` 内。
|
|
988
|
-
-
|
|
989
|
-
-
|
|
990
|
-
- HTTP
|
|
1122
|
+
- 敏感文件名和明显的凭据读取计划会被阻止;`.patchwarden/` 不会绕过该检查。
|
|
1123
|
+
- 任务差异中的疑似密钥值会在落盘前脱敏,并把任务标记为策略违规。
|
|
1124
|
+
- HTTP 服务只绑定 `127.0.0.1`,并拒绝非回环 Host。
|
|
991
1125
|
- Runner 不会自动 commit、push、发布或重置仓库。
|
|
992
1126
|
|
|
993
1127
|
需要保护的本地路径:
|
|
@@ -1081,6 +1215,10 @@ Release 和发布资产校验值。
|
|
|
1081
1215
|
- [ChatGPT 调用规范](docs/chatgpt-usage.md)
|
|
1082
1216
|
- [旧版本迁移指南](docs/migration-from-safe-bifrost.md)
|
|
1083
1217
|
- [ChatGPT Connector 演示](docs/demo.md)
|
|
1218
|
+
- [Dashboard 概览](docs/dashboard-overview.md)
|
|
1219
|
+
- [任务 safe 验收工作流](docs/task-safe-review-workflow.md)
|
|
1220
|
+
- [Lineage 与 Evidence Pack 工作流](docs/lineage-evidence-pack-workflow.md)
|
|
1221
|
+
- [Direct 会话工作流](docs/direct-session-workflow.md)
|
|
1084
1222
|
- [OpenAI Tunnel 示例](examples/openai-tunnel/README.md)
|
|
1085
1223
|
- [ChatGPT 测试提示词](examples/openai-tunnel/chatgpt-test-prompt.md)
|
|
1086
1224
|
|
|
@@ -1096,7 +1234,7 @@ Release 和发布资产校验值。
|
|
|
1096
1234
|
- [x] Release Gate(发布前五阶段校验)
|
|
1097
1235
|
- [x] Worktree 隔离
|
|
1098
1236
|
- [x] 多 Agent 路由
|
|
1099
|
-
- [
|
|
1237
|
+
- [x] 本地 Dashboard
|
|
1100
1238
|
|
|
1101
1239
|
## License
|
|
1102
1240
|
|
|
@@ -12,4 +12,4 @@ export interface AgentAssessorInput {
|
|
|
12
12
|
maxOutputBytes: number;
|
|
13
13
|
config: PatchWardenConfig;
|
|
14
14
|
}
|
|
15
|
-
export declare function runAgentAssessment(input: AgentAssessorInput): AgentAssessmentSummary
|
|
15
|
+
export declare function runAgentAssessment(input: AgentAssessorInput): Promise<AgentAssessmentSummary>;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { resolve, relative, isAbsolute } from "node:path";
|
|
1
|
+
import { resolve, relative, isAbsolute, sep } from "node:path";
|
|
3
2
|
import { buildAgentInvocation, buildAssessmentPrompt } from "../runner/agentInvocation.js";
|
|
4
3
|
import { runSimpleProcessSync } from "../runner/simpleProcess.js";
|
|
5
4
|
import { captureRepoSnapshot, compareSnapshots } from "../runner/changeCapture.js";
|
|
5
|
+
import { atomicWriteFileSync, atomicWriteJsonFileSync } from "../utils/atomicFile.js";
|
|
6
|
+
import { redactSensitiveContent } from "../security/contentRedaction.js";
|
|
6
7
|
const ASSESSMENT_MARKER = "===ASSESSMENT_JSON===";
|
|
7
|
-
export function runAgentAssessment(input) {
|
|
8
|
+
export async function runAgentAssessment(input) {
|
|
8
9
|
const logPaths = {
|
|
9
10
|
stdout: resolve(input.assessmentDir, "agent-assessment-stdout.log"),
|
|
10
11
|
stderr: resolve(input.assessmentDir, "agent-assessment-stderr.log"),
|
|
@@ -26,14 +27,16 @@ export function runAgentAssessment(input) {
|
|
|
26
27
|
log_paths: logPaths,
|
|
27
28
|
};
|
|
28
29
|
// ── 1. Build prompt and write to file for {prompt_file} support ──
|
|
29
|
-
const
|
|
30
|
+
const safeGoal = redactSensitiveContent(input.goal).content.slice(0, 10_000);
|
|
31
|
+
const safePlan = redactSensitiveContent(input.planContent).content.slice(0, 1024 * 1024);
|
|
32
|
+
const assessmentPrompt = buildAssessmentPrompt(safeGoal, safePlan, input.repoPath);
|
|
30
33
|
const promptFilePath = resolve(input.assessmentDir, "agent-assessment-prompt.md");
|
|
31
|
-
|
|
34
|
+
atomicWriteFileSync(promptFilePath, assessmentPrompt);
|
|
32
35
|
logPaths.prompt = promptFilePath;
|
|
33
36
|
// ── 2. Before snapshot (repo-scoped) ──
|
|
34
37
|
let repoBefore;
|
|
35
38
|
try {
|
|
36
|
-
repoBefore = captureRepoSnapshot(input.repoPath);
|
|
39
|
+
repoBefore = await captureRepoSnapshot(input.repoPath);
|
|
37
40
|
}
|
|
38
41
|
catch {
|
|
39
42
|
// If we can't capture before snapshot, conservatively skip agent assessment
|
|
@@ -68,12 +71,14 @@ export function runAgentAssessment(input) {
|
|
|
68
71
|
maxStderrBytes: Math.max(16384, Math.floor(input.maxOutputBytes / 4)),
|
|
69
72
|
stdoutPath: logPaths.stdout,
|
|
70
73
|
stderrPath: logPaths.stderr,
|
|
74
|
+
environmentVariableNames: invocation.environmentVariableNames,
|
|
75
|
+
blockedEnvironmentVariableNames: invocation.blockedEnvironmentVariableNames,
|
|
71
76
|
});
|
|
72
77
|
// ── 5. After snapshot + read-only violation check ──
|
|
73
78
|
let readOnlyViolation = false;
|
|
74
79
|
let violationFiles = [];
|
|
75
80
|
try {
|
|
76
|
-
const repoAfter = captureRepoSnapshot(input.repoPath);
|
|
81
|
+
const repoAfter = await captureRepoSnapshot(input.repoPath);
|
|
77
82
|
const changes = compareSnapshots(repoBefore, repoAfter);
|
|
78
83
|
if (changes.length > 0) {
|
|
79
84
|
readOnlyViolation = true;
|
|
@@ -101,11 +106,11 @@ export function runAgentAssessment(input) {
|
|
|
101
106
|
stderr_truncated: result.stderrTruncated,
|
|
102
107
|
log_paths: { ...logPaths, violation: resolve(input.assessmentDir, "agent-assessment-violation.json") },
|
|
103
108
|
};
|
|
104
|
-
|
|
109
|
+
atomicWriteJsonFileSync(summary.log_paths.violation, {
|
|
105
110
|
violation: "read_only_violation",
|
|
106
111
|
files: violationFiles,
|
|
107
112
|
exit_code: result.exitCode,
|
|
108
|
-
}
|
|
113
|
+
});
|
|
109
114
|
writeSummary(logPaths.assessment, summary);
|
|
110
115
|
return summary;
|
|
111
116
|
}
|
|
@@ -216,13 +221,17 @@ function parseAssessmentJson(stdout, repoPath) {
|
|
|
216
221
|
const jsonEnd = jsonText.lastIndexOf("}");
|
|
217
222
|
if (jsonStart < 0 || jsonEnd < 0 || jsonEnd <= jsonStart)
|
|
218
223
|
return { output: null, sanitized_reasons };
|
|
219
|
-
let
|
|
224
|
+
let parsedValue;
|
|
220
225
|
try {
|
|
221
|
-
|
|
226
|
+
parsedValue = JSON.parse(jsonText.slice(jsonStart, jsonEnd + 1));
|
|
222
227
|
}
|
|
223
228
|
catch {
|
|
224
229
|
return { output: null, sanitized_reasons };
|
|
225
230
|
}
|
|
231
|
+
if (!parsedValue || typeof parsedValue !== "object" || Array.isArray(parsedValue)) {
|
|
232
|
+
return { output: null, sanitized_reasons };
|
|
233
|
+
}
|
|
234
|
+
const parsed = parsedValue;
|
|
226
235
|
// ── Strict validation ──
|
|
227
236
|
if (parsed.risk_level !== "low" && parsed.risk_level !== "medium" && parsed.risk_level !== "high") {
|
|
228
237
|
return { output: null, sanitized_reasons };
|
|
@@ -235,7 +244,7 @@ function parseAssessmentJson(stdout, repoPath) {
|
|
|
235
244
|
return { output: null, sanitized_reasons };
|
|
236
245
|
if (parsed.reason_codes.length > 50)
|
|
237
246
|
return { output: null, sanitized_reasons };
|
|
238
|
-
const reasonCodes = parsed.reason_codes.filter((
|
|
247
|
+
const reasonCodes = parsed.reason_codes.filter((reason) => typeof reason === "string" && reason.length <= 100);
|
|
239
248
|
if (reasonCodes.length !== parsed.reason_codes.length) {
|
|
240
249
|
sanitized_reasons.push("reason_codes_filtered");
|
|
241
250
|
}
|
|
@@ -257,7 +266,7 @@ function parseAssessmentJson(stdout, repoPath) {
|
|
|
257
266
|
}
|
|
258
267
|
const resolved = resolve(repoPath, p);
|
|
259
268
|
const rel = relative(repoPath, resolved);
|
|
260
|
-
if (rel.startsWith(
|
|
269
|
+
if (rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
|
|
261
270
|
pathsSanitized = true;
|
|
262
271
|
continue;
|
|
263
272
|
}
|
|
@@ -270,7 +279,7 @@ function parseAssessmentJson(stdout, repoPath) {
|
|
|
270
279
|
return { output: null, sanitized_reasons };
|
|
271
280
|
if (parsed.destructive_actions.length > 20)
|
|
272
281
|
return { output: null, sanitized_reasons };
|
|
273
|
-
const destructiveActions = parsed.destructive_actions.filter((
|
|
282
|
+
const destructiveActions = parsed.destructive_actions.filter((action) => typeof action === "string" && action.length <= 200);
|
|
274
283
|
if (typeof parsed.requires_user_confirm !== "boolean")
|
|
275
284
|
return { output: null, sanitized_reasons };
|
|
276
285
|
const notes = typeof parsed.notes === "string" ? parsed.notes.slice(0, 2000) : "";
|
|
@@ -289,5 +298,5 @@ function parseAssessmentJson(stdout, repoPath) {
|
|
|
289
298
|
};
|
|
290
299
|
}
|
|
291
300
|
function writeSummary(path, summary) {
|
|
292
|
-
|
|
301
|
+
atomicWriteJsonFileSync(path, summary);
|
|
293
302
|
}
|
|
@@ -117,7 +117,7 @@ export declare function validateAssessmentFreshness(assessmentId: string, curren
|
|
|
117
117
|
* This is intentionally not registered as an MCP tool: a remote client may
|
|
118
118
|
* request confirmation, but it cannot grant confirmation to itself.
|
|
119
119
|
*/
|
|
120
|
-
export declare function confirmAssessment(assessmentId: string): AssessmentConfirmationResult
|
|
120
|
+
export declare function confirmAssessment(assessmentId: string): Promise<AssessmentConfirmationResult>;
|
|
121
121
|
export declare function computeWorkspaceFingerprint(snapshot: RepoSnapshot): string;
|
|
122
122
|
interface PolicyHashInput {
|
|
123
123
|
change_policy: ChangePolicy;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import { mkdirSync,
|
|
1
|
+
import { mkdirSync, existsSync, readFileSync, } from "node:fs";
|
|
2
2
|
import { createHash, randomBytes } from "node:crypto";
|
|
3
3
|
import { resolve, join } from "node:path";
|
|
4
4
|
import { getAssessmentsDir, getConfig, getRepoAllowedTestCommands } from "../config.js";
|
|
5
5
|
import { guardPath } from "../security/pathGuard.js";
|
|
6
6
|
import { PatchWardenError } from "../errors.js";
|
|
7
|
+
import { redactSensitiveContent } from "../security/contentRedaction.js";
|
|
8
|
+
import { atomicWriteJsonFileSync } from "../utils/atomicFile.js";
|
|
9
|
+
import { withFileLockSync } from "../utils/lockedJsonFile.js";
|
|
7
10
|
import { TOOL_SCHEMA_EPOCH } from "../version.js";
|
|
8
|
-
import { getLastToolCatalogSnapshot } from "../tools/toolCatalog.js";
|
|
11
|
+
import { getLastToolCatalogSnapshot } from "../tools/catalog/toolCatalog.js";
|
|
9
12
|
import { captureRepoSnapshot } from "../runner/changeCapture.js";
|
|
10
13
|
const SENSITIVE_PATH_RULES_SIGNATURE = "v1";
|
|
11
14
|
const ARTIFACT_RULES_SIGNATURE = "v1";
|
|
@@ -71,7 +74,7 @@ export function createAssessment(input) {
|
|
|
71
74
|
repo_path: input.repo_path,
|
|
72
75
|
resolved_repo_path: input.resolved_repo_path,
|
|
73
76
|
template: input.template || null,
|
|
74
|
-
goal: input.goal
|
|
77
|
+
goal: input.goal ? redactSensitiveContent(input.goal).content : null,
|
|
75
78
|
test_command: input.test_command || null,
|
|
76
79
|
verify_commands: input.verify_commands || [],
|
|
77
80
|
agent: input.agent,
|
|
@@ -83,7 +86,7 @@ export function createAssessment(input) {
|
|
|
83
86
|
confirm_code: null,
|
|
84
87
|
agent_assessment_summary: input.agent_assessment_summary || null,
|
|
85
88
|
};
|
|
86
|
-
|
|
89
|
+
atomicWriteJsonFileSync(join(dir, "assessment.json"), record);
|
|
87
90
|
return record;
|
|
88
91
|
}
|
|
89
92
|
export function readAssessment(assessmentId) {
|
|
@@ -156,48 +159,53 @@ export function validateAssessmentFreshness(assessmentId, currentSnapshot, optio
|
|
|
156
159
|
* This is intentionally not registered as an MCP tool: a remote client may
|
|
157
160
|
* request confirmation, but it cannot grant confirmation to itself.
|
|
158
161
|
*/
|
|
159
|
-
export function confirmAssessment(assessmentId) {
|
|
162
|
+
export async function confirmAssessment(assessmentId) {
|
|
160
163
|
if (!/^assessment_\d{8}_\d{6}_[0-9a-f]{32}$/.test(assessmentId)) {
|
|
161
164
|
throw new PatchWardenError("assessment_id_invalid", "Local confirmation requires the full assessment_id (32 hexadecimal random characters).", "Copy the complete assessment_id from the assess_only response; assessment_short_id is display-only.");
|
|
162
165
|
}
|
|
166
|
+
const config = getConfig();
|
|
167
|
+
const assessmentFile = resolve(getAssessmentsDir(config), assessmentId, "assessment.json");
|
|
168
|
+
guardPath(assessmentFile, config.workspaceRoot, config.assessmentsDir);
|
|
163
169
|
const assessment = readAssessment(assessmentId);
|
|
164
170
|
if (assessment.decision !== "needs_confirm" || !assessment.requires_confirm) {
|
|
165
171
|
throw new PatchWardenError("assessment_confirmation_not_allowed", `Assessment "${assessmentId}" has decision "${assessment.decision}" and cannot be locally confirmed.`, assessment.decision === "blocked"
|
|
166
172
|
? "Fix the hard-rule finding and run assess_only again."
|
|
167
173
|
: "This assessment does not require confirmation; execute it with the minimal assessment_id call.");
|
|
168
174
|
}
|
|
169
|
-
const snapshot = captureRepoSnapshot(assessment.resolved_repo_path);
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
+
const snapshot = await captureRepoSnapshot(assessment.resolved_repo_path);
|
|
176
|
+
// Re-read and write while holding one cross-process lock. Concurrent local
|
|
177
|
+
// confirmers may compute snapshots in parallel, but they cannot replace one
|
|
178
|
+
// another's confirmation metadata from a stale assessment record.
|
|
179
|
+
return withFileLockSync(assessmentFile, () => {
|
|
180
|
+
const validation = validateAssessmentFreshness(assessmentId, snapshot, {
|
|
181
|
+
allow_unconfirmed: true,
|
|
182
|
+
// A standalone CLI has no active MCP profile snapshot. The execute path
|
|
183
|
+
// still performs the full tool-manifest check before creating the task.
|
|
184
|
+
skip_tool_manifest: true,
|
|
185
|
+
});
|
|
186
|
+
if (!validation.valid || !validation.assessment) {
|
|
187
|
+
throw new PatchWardenError(validation.failure_reason || "assessment_validation_failed", `Assessment "${assessmentId}" cannot be confirmed: ${validation.failure_reason || "validation_failed"}.`, "Run create_task with execution_mode=assess_only again, then confirm the new assessment locally.");
|
|
188
|
+
}
|
|
189
|
+
if (validation.assessment.decision !== "needs_confirm" || !validation.assessment.requires_confirm) {
|
|
190
|
+
throw new PatchWardenError("assessment_confirmation_not_allowed", `Assessment "${assessmentId}" is no longer eligible for local confirmation.`, "Run create_task with execution_mode=assess_only again.");
|
|
191
|
+
}
|
|
192
|
+
const confirmedAt = validation.assessment.confirmed_at || new Date().toISOString();
|
|
193
|
+
const confirmedRecord = {
|
|
194
|
+
...validation.assessment,
|
|
195
|
+
confirmed: true,
|
|
196
|
+
confirmed_at: confirmedAt,
|
|
197
|
+
confirm_code: validation.assessment.confirm_code || randomBytes(8).toString("hex"),
|
|
198
|
+
};
|
|
199
|
+
atomicWriteJsonFileSync(assessmentFile, confirmedRecord);
|
|
200
|
+
return {
|
|
201
|
+
assessment_id: assessmentId,
|
|
202
|
+
decision: "needs_confirm",
|
|
203
|
+
confirmed: true,
|
|
204
|
+
confirmed_at: confirmedAt,
|
|
205
|
+
expires_at: confirmedRecord.expires_at,
|
|
206
|
+
next_action: "Call create_task with only execution_mode=execute and this full assessment_id.",
|
|
207
|
+
};
|
|
175
208
|
});
|
|
176
|
-
if (!validation.valid || !validation.assessment) {
|
|
177
|
-
throw new PatchWardenError(validation.failure_reason || "assessment_validation_failed", `Assessment "${assessmentId}" cannot be confirmed: ${validation.failure_reason || "validation_failed"}.`, "Run create_task with execution_mode=assess_only again, then confirm the new assessment locally.");
|
|
178
|
-
}
|
|
179
|
-
const confirmedAt = validation.assessment.confirmed_at || new Date().toISOString();
|
|
180
|
-
const confirmedRecord = {
|
|
181
|
-
...validation.assessment,
|
|
182
|
-
confirmed: true,
|
|
183
|
-
confirmed_at: confirmedAt,
|
|
184
|
-
confirm_code: validation.assessment.confirm_code || randomBytes(8).toString("hex"),
|
|
185
|
-
};
|
|
186
|
-
const config = getConfig();
|
|
187
|
-
const assessmentFile = resolve(getAssessmentsDir(config), assessmentId, "assessment.json");
|
|
188
|
-
guardPath(assessmentFile, config.workspaceRoot, config.assessmentsDir);
|
|
189
|
-
const temporaryFile = `${assessmentFile}.${process.pid}.${randomBytes(4).toString("hex")}.tmp`;
|
|
190
|
-
guardPath(temporaryFile, config.workspaceRoot, config.assessmentsDir);
|
|
191
|
-
writeFileSync(temporaryFile, JSON.stringify(confirmedRecord, null, 2), "utf-8");
|
|
192
|
-
renameSync(temporaryFile, assessmentFile);
|
|
193
|
-
return {
|
|
194
|
-
assessment_id: assessmentId,
|
|
195
|
-
decision: "needs_confirm",
|
|
196
|
-
confirmed: true,
|
|
197
|
-
confirmed_at: confirmedAt,
|
|
198
|
-
expires_at: confirmedRecord.expires_at,
|
|
199
|
-
next_action: "Call create_task with only execution_mode=execute and this full assessment_id.",
|
|
200
|
-
};
|
|
201
209
|
}
|
|
202
210
|
export function computeWorkspaceFingerprint(snapshot) {
|
|
203
211
|
const fileHashes = Object.entries(snapshot.files)
|
|
@@ -9,18 +9,19 @@
|
|
|
9
9
|
import { loadConfig } from "../config.js";
|
|
10
10
|
import { confirmAssessment } from "./assessmentStore.js";
|
|
11
11
|
import { errorPayload } from "../errors.js";
|
|
12
|
+
import { logger } from "../logging.js";
|
|
12
13
|
loadConfig();
|
|
13
14
|
const assessmentId = process.argv[2];
|
|
14
15
|
if (!assessmentId || process.argv.length !== 3) {
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
logger.info("Usage: patchwarden-confirm <full_assessment_id>");
|
|
17
|
+
logger.info("The display-only assessment_short_id is not accepted.");
|
|
17
18
|
process.exit(1);
|
|
18
19
|
}
|
|
19
20
|
try {
|
|
20
|
-
const result = confirmAssessment(assessmentId);
|
|
21
|
+
const result = await confirmAssessment(assessmentId);
|
|
21
22
|
console.log(JSON.stringify(result, null, 2));
|
|
22
23
|
}
|
|
23
24
|
catch (error) {
|
|
24
|
-
|
|
25
|
+
logger.error("confirm_assessment_failed", { error: errorPayload(error) });
|
|
25
26
|
process.exit(1);
|
|
26
27
|
}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export interface AgentConfig {
|
|
2
2
|
command: string;
|
|
3
3
|
args: string[];
|
|
4
|
+
envAllowlist?: string[];
|
|
5
|
+
adapter?: string;
|
|
6
|
+
model?: string;
|
|
4
7
|
}
|
|
5
8
|
export interface PatchWardenConfig {
|
|
6
9
|
workspaceRoot: string;
|
|
@@ -17,11 +20,30 @@ export interface PatchWardenConfig {
|
|
|
17
20
|
watcherStaleSeconds: number;
|
|
18
21
|
toolProfile?: "full" | "chatgpt_core" | "chatgpt_direct" | "chatgpt_search";
|
|
19
22
|
repoAliases?: Record<string, string>;
|
|
23
|
+
httpPort?: number;
|
|
24
|
+
http?: {
|
|
25
|
+
port?: number;
|
|
26
|
+
host?: string;
|
|
27
|
+
ownerTokenEnv?: string;
|
|
28
|
+
};
|
|
29
|
+
enableRunTaskTool?: boolean;
|
|
20
30
|
enableAgentAssessment?: boolean;
|
|
21
31
|
agentAssessmentTimeoutSeconds?: number;
|
|
22
32
|
agentAssessmentMaxOutputBytes?: number;
|
|
23
33
|
agentAssessmentAgentName?: string;
|
|
24
34
|
enableDirectProfile?: boolean;
|
|
35
|
+
tunnelClientPath?: string;
|
|
36
|
+
tunnelProxy?: {
|
|
37
|
+
scope: "shared" | "separate";
|
|
38
|
+
core: {
|
|
39
|
+
mode: "environment" | "none" | "manual";
|
|
40
|
+
url?: string;
|
|
41
|
+
};
|
|
42
|
+
direct: {
|
|
43
|
+
mode: "environment" | "none" | "manual";
|
|
44
|
+
url?: string;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
25
47
|
directSessionsDir: string;
|
|
26
48
|
directSessionTtlSeconds: number;
|
|
27
49
|
directMaxPatchBytes: number;
|