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.
Files changed (591) hide show
  1. package/PatchWarden.cmd +32 -2
  2. package/README.en.md +158 -26
  3. package/README.md +124 -24
  4. package/dist/assessments/agentAssessor.js +21 -12
  5. package/dist/assessments/assessmentDiagnostics.d.ts +29 -0
  6. package/dist/assessments/assessmentDiagnostics.js +132 -0
  7. package/dist/assessments/assessmentStore.d.ts +39 -0
  8. package/dist/assessments/assessmentStore.js +207 -45
  9. package/dist/assessments/securitySnapshot.d.ts +50 -0
  10. package/dist/assessments/securitySnapshot.js +158 -0
  11. package/dist/config.d.ts +15 -0
  12. package/dist/config.js +66 -13
  13. package/dist/control/fileManager.d.ts +2 -0
  14. package/dist/control/fileManager.js +39 -0
  15. package/dist/control/middleware/auth.d.ts +6 -0
  16. package/dist/control/middleware/auth.js +14 -5
  17. package/dist/control/middleware/static.js +16 -3
  18. package/dist/control/routeTable.d.ts +24 -0
  19. package/dist/control/routeTable.js +263 -0
  20. package/dist/control/routes/audit.js +17 -7
  21. package/dist/control/routes/evidence.js +1 -1
  22. package/dist/control/routes/lineage.js +1 -1
  23. package/dist/control/routes/process.d.ts +7 -0
  24. package/dist/control/routes/process.js +329 -42
  25. package/dist/control/routes/sessions.d.ts +9 -1
  26. package/dist/control/routes/sessions.js +84 -21
  27. package/dist/control/routes/status.d.ts +60 -1
  28. package/dist/control/routes/status.js +94 -15
  29. package/dist/control/routes/taskActions.js +24 -60
  30. package/dist/control/routes/tasks.d.ts +4 -0
  31. package/dist/control/routes/tasks.js +61 -62
  32. package/dist/control/routes/workspace.js +50 -23
  33. package/dist/control/runtime.d.ts +3 -2
  34. package/dist/control/runtime.js +65 -40
  35. package/dist/control/server.js +37 -365
  36. package/dist/control/shared.d.ts +25 -1
  37. package/dist/control/shared.js +64 -11
  38. package/dist/diagnostics/allowedTestCommandSafety.d.ts +1 -0
  39. package/dist/diagnostics/allowedTestCommandSafety.js +11 -0
  40. package/dist/direct/directAudit.js +234 -225
  41. package/dist/direct/directGuards.d.ts +2 -1
  42. package/dist/direct/directGuards.js +42 -29
  43. package/dist/direct/directPatch.d.ts +6 -1
  44. package/dist/direct/directPatch.js +55 -8
  45. package/dist/direct/directSessionStore.d.ts +16 -6
  46. package/dist/direct/directSessionStore.js +128 -35
  47. package/dist/direct/directVerification.js +8 -16
  48. package/dist/doctor.js +65 -57
  49. package/dist/goal/acceptanceEngine.d.ts +1 -1
  50. package/dist/goal/goalProgress.js +75 -69
  51. package/dist/goal/goalReport.js +5 -12
  52. package/dist/goal/goalStatus.d.ts +2 -0
  53. package/dist/goal/goalStatus.js +10 -3
  54. package/dist/goal/goalStore.d.ts +9 -2
  55. package/dist/goal/goalStore.js +152 -45
  56. package/dist/goal/handoffExport.js +6 -6
  57. package/dist/goal/specKitImport.js +97 -96
  58. package/dist/goal/subgoalSync.js +11 -9
  59. package/dist/goal/worktreeManager.d.ts +2 -0
  60. package/dist/goal/worktreeManager.js +209 -136
  61. package/dist/httpServer.js +111 -32
  62. package/dist/logging.js +5 -3
  63. package/dist/policy/projectPolicy.js +5 -2
  64. package/dist/release/releaseGate.d.ts +1 -1
  65. package/dist/release/releaseGate.js +78 -25
  66. package/dist/runner/agentInvocation.d.ts +14 -0
  67. package/dist/runner/agentInvocation.js +96 -4
  68. package/dist/runner/changeCapture.d.ts +13 -5
  69. package/dist/runner/changeCapture.js +243 -61
  70. package/dist/runner/postTaskCleanup.js +48 -19
  71. package/dist/runner/processSecurity.d.ts +56 -0
  72. package/dist/runner/processSecurity.js +373 -0
  73. package/dist/runner/runTask.d.ts +2 -1
  74. package/dist/runner/runTask.js +162 -93
  75. package/dist/runner/simpleProcess.d.ts +3 -0
  76. package/dist/runner/simpleProcess.js +50 -36
  77. package/dist/{taskProgress.d.ts → runner/taskProgress.d.ts} +1 -1
  78. package/dist/{taskProgress.js → runner/taskProgress.js} +2 -2
  79. package/dist/{taskRuntime.d.ts → runner/taskRuntime.d.ts} +3 -1
  80. package/dist/{taskRuntime.js → runner/taskRuntime.js} +3 -2
  81. package/dist/runner/taskStatusStore.d.ts +11 -0
  82. package/dist/runner/taskStatusStore.js +35 -0
  83. package/dist/runner/watch.d.ts +14 -1
  84. package/dist/runner/watch.js +320 -49
  85. package/dist/security/contentRedaction.js +2 -2
  86. package/dist/security/discoveryTokenStore.d.ts +3 -2
  87. package/dist/security/discoveryTokenStore.js +17 -2
  88. package/dist/security/loopbackHost.d.ts +2 -0
  89. package/dist/security/loopbackHost.js +16 -0
  90. package/dist/security/pathGuard.js +32 -17
  91. package/dist/security/planGuard.js +2 -1
  92. package/dist/security/runtimeGuard.js +9 -6
  93. package/dist/security/secretComparison.d.ts +3 -0
  94. package/dist/security/secretComparison.js +11 -0
  95. package/dist/security/sensitiveGuard.d.ts +5 -0
  96. package/dist/security/sensitiveGuard.js +27 -8
  97. package/dist/security/toolInvocationGuard.d.ts +2 -2
  98. package/dist/security/toolInvocationGuard.js +1 -1
  99. package/dist/security/workspaceRootGuard.d.ts +8 -0
  100. package/dist/security/workspaceRootGuard.js +47 -0
  101. package/dist/smoke-test.js +27 -26
  102. package/dist/tools/catalog/index.d.ts +4 -0
  103. package/dist/tools/catalog/index.js +4 -0
  104. package/dist/tools/{toolCatalog.js → catalog/toolCatalog.js} +3 -12
  105. package/dist/tools/{toolRegistry.d.ts → catalog/toolRegistry.d.ts} +3 -3
  106. package/dist/tools/{toolRegistry.js → catalog/toolRegistry.js} +6 -15
  107. package/dist/tools/definitions/toolDefs.d.ts +18 -0
  108. package/dist/tools/definitions/toolDefs.js +1177 -0
  109. package/dist/tools/{auditSession.d.ts → diagnostics/auditSession.d.ts} +1 -1
  110. package/dist/tools/{auditSession.js → diagnostics/auditSession.js} +2 -2
  111. package/dist/tools/{auditTask.d.ts → diagnostics/auditTask.d.ts} +2 -1
  112. package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +134 -36
  113. package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +18 -6
  114. package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +36 -8
  115. package/dist/tools/diagnostics/index.d.ts +6 -0
  116. package/dist/tools/diagnostics/index.js +6 -0
  117. package/dist/tools/{safeStatus.d.ts → diagnostics/safeStatus.d.ts} +3 -3
  118. package/dist/tools/{safeStatus.js → diagnostics/safeStatus.js} +6 -6
  119. package/dist/tools/{safeViews.d.ts → diagnostics/safeViews.d.ts} +12 -8
  120. package/dist/tools/{safeViews.js → diagnostics/safeViews.js} +23 -13
  121. package/dist/tools/{schemaDriftCheck.d.ts → diagnostics/schemaDriftCheck.d.ts} +1 -1
  122. package/dist/tools/{schemaDriftCheck.js → diagnostics/schemaDriftCheck.js} +2 -2
  123. package/dist/tools/{createDirectSession.js → direct/createDirectSession.js} +6 -6
  124. package/dist/tools/{finalizeDirectSession.d.ts → direct/finalizeDirectSession.d.ts} +1 -1
  125. package/dist/tools/direct/finalizeDirectSession.js +86 -0
  126. package/dist/tools/direct/index.d.ts +3 -0
  127. package/dist/tools/direct/index.js +3 -0
  128. package/dist/tools/{runDirectVerificationBundle.js → direct/runDirectVerificationBundle.js} +2 -2
  129. package/dist/tools/{discoverTools.d.ts → discovery/discoverTools.d.ts} +2 -2
  130. package/dist/tools/{discoverTools.js → discovery/discoverTools.js} +5 -5
  131. package/dist/tools/{explainTool.d.ts → discovery/explainTool.d.ts} +2 -2
  132. package/dist/tools/{explainTool.js → discovery/explainTool.js} +2 -2
  133. package/dist/tools/discovery/index.d.ts +3 -0
  134. package/dist/tools/discovery/index.js +3 -0
  135. package/dist/tools/{invokeDiscoveredTool.d.ts → discovery/invokeDiscoveredTool.d.ts} +2 -2
  136. package/dist/tools/{invokeDiscoveredTool.js → discovery/invokeDiscoveredTool.js} +5 -5
  137. package/dist/tools/dispatch/coreDispatch.js +32 -31
  138. package/dist/tools/dispatch/diagnosticDispatch.d.ts +3 -5
  139. package/dist/tools/dispatch/diagnosticDispatch.js +70 -68
  140. package/dist/tools/dispatch/directDispatch.js +11 -10
  141. package/dist/tools/dispatch/goalDispatch.js +5 -4
  142. package/dist/tools/dispatch/releaseDispatch.js +3 -2
  143. package/dist/tools/dispatch/validation.d.ts +7 -0
  144. package/dist/tools/dispatch/validation.js +71 -0
  145. package/dist/tools/{getPlan.js → goals/getPlan.js} +4 -4
  146. package/dist/tools/{goalSubgoalTask.d.ts → goals/goalSubgoalTask.d.ts} +1 -1
  147. package/dist/tools/goals/goalSubgoalTask.js +118 -0
  148. package/dist/tools/goals/index.d.ts +3 -0
  149. package/dist/tools/goals/index.js +3 -0
  150. package/dist/tools/{savePlan.js → goals/savePlan.js} +28 -8
  151. package/dist/tools/registry.d.ts +7 -11
  152. package/dist/tools/registry.js +17 -1178
  153. package/dist/tools/{checkReleaseGate.d.ts → release/checkReleaseGate.d.ts} +1 -1
  154. package/dist/tools/{checkReleaseGate.js → release/checkReleaseGate.js} +3 -3
  155. package/dist/tools/release/index.d.ts +2 -0
  156. package/dist/tools/release/index.js +2 -0
  157. package/dist/tools/{releaseMode.d.ts → release/releaseMode.d.ts} +2 -2
  158. package/dist/tools/{releaseMode.js → release/releaseMode.js} +46 -26
  159. package/dist/tools/tasks/cancelTask.d.ts +11 -0
  160. package/dist/tools/tasks/cancelTask.js +86 -0
  161. package/dist/tools/{createTask.d.ts → tasks/createTask.d.ts} +12 -4
  162. package/dist/tools/{createTask.js → tasks/createTask.js} +110 -34
  163. package/dist/tools/{diagnoseTask.d.ts → tasks/diagnoseTask.d.ts} +1 -1
  164. package/dist/tools/{diagnoseTask.js → tasks/diagnoseTask.js} +12 -16
  165. package/dist/tools/{evidencePack.js → tasks/evidencePack.js} +34 -22
  166. package/dist/tools/{getTaskFile.js → tasks/getTaskFile.js} +4 -4
  167. package/dist/tools/{getTaskStatus.d.ts → tasks/getTaskStatus.d.ts} +6 -1
  168. package/dist/tools/{getTaskStatus.js → tasks/getTaskStatus.js} +11 -6
  169. package/dist/tools/{getTaskStdoutTail.js → tasks/getTaskStdoutTail.js} +13 -11
  170. package/dist/tools/{getTaskSummary.js → tasks/getTaskSummary.js} +35 -36
  171. package/dist/tools/tasks/index.d.ts +18 -0
  172. package/dist/tools/tasks/index.js +18 -0
  173. package/dist/tools/tasks/killTask.d.ts +1 -0
  174. package/dist/tools/{listTasks.d.ts → tasks/listTasks.d.ts} +1 -1
  175. package/dist/tools/{listTasks.js → tasks/listTasks.js} +5 -4
  176. package/dist/tools/{reconcileTasks.d.ts → tasks/reconcileTasks.d.ts} +3 -2
  177. package/dist/tools/tasks/reconcileTasks.js +496 -0
  178. package/dist/tools/tasks/retryTask.js +60 -0
  179. package/dist/tools/{runTaskLoop.d.ts → tasks/runTaskLoop.d.ts} +6 -6
  180. package/dist/tools/{runTaskLoop.js → tasks/runTaskLoop.js} +63 -41
  181. package/dist/tools/tasks/runVerification.js +38 -0
  182. package/dist/tools/{taskLineage.js → tasks/taskLineage.js} +8 -7
  183. package/dist/tools/{taskOutputs.d.ts → tasks/taskOutputs.d.ts} +2 -2
  184. package/dist/tools/{taskOutputs.js → tasks/taskOutputs.js} +17 -16
  185. package/dist/tools/tasks/taskStates.d.ts +6 -0
  186. package/dist/tools/tasks/taskStates.js +52 -0
  187. package/dist/tools/{waitForTask.js → tasks/waitForTask.js} +3 -11
  188. package/dist/tools/{androidDoctor.js → workspace/androidDoctor.js} +16 -10
  189. package/dist/tools/{applyPatch.d.ts → workspace/applyPatch.d.ts} +1 -1
  190. package/dist/tools/workspace/applyPatch.js +53 -0
  191. package/dist/tools/{discardWorktree.js → workspace/discardWorktree.js} +3 -3
  192. package/dist/tools/workspace/index.d.ts +10 -0
  193. package/dist/tools/workspace/index.js +10 -0
  194. package/dist/tools/workspace/listAgents.d.ts +23 -0
  195. package/dist/tools/workspace/listAgents.js +56 -0
  196. package/dist/tools/{listWorkspace.js → workspace/listWorkspace.js} +3 -3
  197. package/dist/tools/{mergeWorktree.js → workspace/mergeWorktree.js} +3 -3
  198. package/dist/tools/{readWorkspaceFile.js → workspace/readWorkspaceFile.js} +8 -8
  199. package/dist/tools/{recommendAgentForTask.js → workspace/recommendAgentForTask.js} +3 -3
  200. package/dist/tools/{searchWorkspace.js → workspace/searchWorkspace.js} +5 -5
  201. package/dist/tools/{syncFile.d.ts → workspace/syncFile.d.ts} +1 -1
  202. package/dist/tools/workspace/syncFile.js +134 -0
  203. package/dist/utils/atomicFile.d.ts +7 -0
  204. package/dist/utils/atomicFile.js +67 -0
  205. package/dist/utils/boundedFile.d.ts +8 -0
  206. package/dist/utils/boundedFile.js +93 -0
  207. package/dist/utils/lockedJsonFile.d.ts +15 -0
  208. package/dist/utils/lockedJsonFile.js +224 -0
  209. package/dist/utils/platform.d.ts +11 -0
  210. package/dist/utils/platform.js +26 -0
  211. package/dist/utils/stableJson.d.ts +2 -0
  212. package/dist/utils/stableJson.js +12 -0
  213. package/dist/version.d.ts +3 -2
  214. package/dist/version.js +3 -2
  215. package/dist/watcherStatus.d.ts +2 -2
  216. package/dist/watcherStatus.js +18 -0
  217. package/docs/CODE_WIKI.md +808 -143
  218. package/docs/architecture.md +15 -55
  219. package/docs/assets/patchwarden-desktop-onboarding.png +0 -0
  220. package/docs/control-center/README.md +4 -4
  221. package/docs/dashboard-overview.md +4 -4
  222. package/docs/desktop-app.md +175 -0
  223. package/docs/evidence-pack-schema.md +3 -3
  224. package/docs/open-source-application.md +16 -11
  225. package/docs/release-checklist.md +1 -1
  226. package/docs/release-evidence.md +35 -39
  227. package/docs/task-safe-review-workflow.md +5 -5
  228. package/docs/threat-model.md +19 -1
  229. package/examples/config.example.json +4 -2
  230. package/package.json +27 -10
  231. package/scripts/README.md +15 -6
  232. package/scripts/build.js +11 -0
  233. package/scripts/checks/brand-check.js +3 -0
  234. package/scripts/checks/build-output-check.js +37 -0
  235. package/scripts/checks/control-center-smoke.js +102 -10
  236. package/scripts/checks/control-smoke.js +19 -8
  237. package/scripts/checks/http-mcp-smoke.js +82 -4
  238. package/scripts/checks/lifecycle-smoke.js +15 -12
  239. package/scripts/checks/mcp-manifest-check.js +1 -1
  240. package/scripts/checks/mcp-smoke.js +58 -4
  241. package/scripts/checks/package-manifest-check.js +20 -7
  242. package/scripts/checks/unit-tests.js +3 -5
  243. package/scripts/checks/watcher-supervisor-smoke.js +38 -1
  244. package/scripts/control/get-patchwarden-health.ps1 +1 -1
  245. package/scripts/control/manage-patchwarden.ps1 +69 -4
  246. package/scripts/control/provision-patchwarden-tunnel.ps1 +127 -0
  247. package/scripts/control/run-background-supervisor.ps1 +41 -0
  248. package/scripts/control/start-patchwarden-tunnel.ps1 +191 -33
  249. package/scripts/e2e/demo-runtime.mjs +153 -0
  250. package/{PatchWarden-Control-Tray.cmd → scripts/launchers/PatchWarden-Control-Tray.cmd} +3 -3
  251. package/scripts/launchers/PatchWarden-Control.cmd +6 -0
  252. package/{PatchWarden-Desktop.cmd → scripts/launchers/PatchWarden-Desktop.cmd} +2 -2
  253. package/scripts/launchers/Restart-PatchWarden-Control.cmd +6 -0
  254. package/{Stop-PatchWarden.cmd → scripts/launchers/Stop-PatchWarden.cmd} +2 -2
  255. package/scripts/lib/clean-generated-output.js +11 -0
  256. package/scripts/lib/file-discovery.js +12 -0
  257. package/scripts/release/desktop-preflight.js +211 -0
  258. package/scripts/release/pack-clean.js +59 -14
  259. package/src/assessments/agentAssessor.ts +26 -12
  260. package/src/assessments/assessmentDiagnostics.ts +172 -0
  261. package/src/assessments/assessmentStore.ts +288 -51
  262. package/src/assessments/securitySnapshot.ts +231 -0
  263. package/src/config.ts +70 -13
  264. package/src/control/fileManager.ts +40 -0
  265. package/src/control/middleware/auth.ts +22 -4
  266. package/src/control/middleware/static.ts +16 -3
  267. package/src/control/routeTable.ts +301 -0
  268. package/src/control/routes/audit.ts +14 -6
  269. package/src/control/routes/evidence.ts +1 -1
  270. package/src/control/routes/lineage.ts +1 -1
  271. package/src/control/routes/process.ts +341 -40
  272. package/src/control/routes/sessions.ts +88 -25
  273. package/src/control/routes/status.ts +134 -17
  274. package/src/control/routes/taskActions.ts +24 -58
  275. package/src/control/routes/tasks.ts +66 -58
  276. package/src/control/routes/workspace.ts +56 -25
  277. package/src/control/runtime.ts +71 -43
  278. package/src/control/server.ts +37 -342
  279. package/src/control/shared.ts +77 -11
  280. package/src/diagnostics/allowedTestCommandSafety.ts +12 -0
  281. package/src/direct/directAudit.ts +17 -9
  282. package/src/direct/directGuards.ts +72 -44
  283. package/src/direct/directPatch.ts +95 -8
  284. package/src/direct/directSessionStore.ts +213 -55
  285. package/src/direct/directVerification.ts +9 -23
  286. package/src/doctor.ts +76 -64
  287. package/src/goal/acceptanceEngine.ts +1 -1
  288. package/src/goal/goalProgress.ts +17 -13
  289. package/src/goal/goalReport.ts +5 -13
  290. package/src/goal/goalStatus.ts +19 -3
  291. package/src/goal/goalStore.ts +241 -54
  292. package/src/goal/handoffExport.ts +6 -6
  293. package/src/goal/specKitImport.ts +12 -12
  294. package/src/goal/subgoalSync.ts +11 -8
  295. package/src/goal/worktreeManager.ts +139 -22
  296. package/src/httpServer.ts +113 -30
  297. package/src/logging.ts +10 -3
  298. package/src/policy/projectPolicy.ts +5 -2
  299. package/src/release/releaseGate.ts +74 -27
  300. package/src/runner/agentInvocation.ts +126 -4
  301. package/src/runner/changeCapture.ts +291 -57
  302. package/src/runner/postTaskCleanup.ts +41 -17
  303. package/src/runner/processSecurity.ts +433 -0
  304. package/src/runner/runTask.ts +197 -97
  305. package/src/runner/simpleProcess.ts +66 -31
  306. package/src/{taskProgress.ts → runner/taskProgress.ts} +3 -3
  307. package/src/{taskRuntime.ts → runner/taskRuntime.ts} +6 -3
  308. package/src/runner/taskStatusStore.ts +69 -0
  309. package/src/runner/watch.ts +348 -52
  310. package/src/security/contentRedaction.ts +2 -2
  311. package/src/security/discoveryTokenStore.ts +18 -4
  312. package/src/security/loopbackHost.ts +19 -0
  313. package/src/security/pathGuard.ts +35 -16
  314. package/src/security/planGuard.ts +2 -1
  315. package/src/security/runtimeGuard.ts +9 -6
  316. package/src/security/secretComparison.ts +13 -0
  317. package/src/security/sensitiveGuard.ts +27 -8
  318. package/src/security/toolInvocationGuard.ts +2 -2
  319. package/src/security/workspaceRootGuard.ts +53 -0
  320. package/src/smoke-test.ts +27 -26
  321. package/src/tools/catalog/index.ts +4 -0
  322. package/src/tools/{toolCatalog.ts → catalog/toolCatalog.ts} +3 -12
  323. package/src/tools/{toolRegistry.ts → catalog/toolRegistry.ts} +7 -16
  324. package/src/tools/definitions/toolDefs.ts +1264 -0
  325. package/src/tools/{auditSession.ts → diagnostics/auditSession.ts} +2 -2
  326. package/src/tools/{auditTask.ts → diagnostics/auditTask.ts} +136 -39
  327. package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +37 -9
  328. package/src/tools/diagnostics/index.ts +6 -0
  329. package/src/tools/{safeStatus.ts → diagnostics/safeStatus.ts} +7 -7
  330. package/src/tools/{safeViews.ts → diagnostics/safeViews.ts} +27 -17
  331. package/src/tools/{schemaDriftCheck.ts → diagnostics/schemaDriftCheck.ts} +3 -3
  332. package/src/tools/{createDirectSession.ts → direct/createDirectSession.ts} +6 -6
  333. package/src/tools/{finalizeDirectSession.ts → direct/finalizeDirectSession.ts} +7 -5
  334. package/src/tools/direct/index.ts +3 -0
  335. package/src/tools/{runDirectVerificationBundle.ts → direct/runDirectVerificationBundle.ts} +2 -2
  336. package/src/tools/{discoverTools.ts → discovery/discoverTools.ts} +6 -6
  337. package/src/tools/{explainTool.ts → discovery/explainTool.ts} +3 -3
  338. package/src/tools/discovery/index.ts +3 -0
  339. package/src/tools/{invokeDiscoveredTool.ts → discovery/invokeDiscoveredTool.ts} +7 -7
  340. package/src/tools/dispatch/coreDispatch.ts +36 -31
  341. package/src/tools/dispatch/diagnosticDispatch.ts +26 -21
  342. package/src/tools/dispatch/directDispatch.ts +11 -10
  343. package/src/tools/dispatch/goalDispatch.ts +5 -4
  344. package/src/tools/dispatch/releaseDispatch.ts +3 -2
  345. package/src/tools/dispatch/validation.ts +78 -0
  346. package/src/tools/{getPlan.ts → goals/getPlan.ts} +4 -4
  347. package/src/tools/{goalSubgoalTask.ts → goals/goalSubgoalTask.ts} +41 -26
  348. package/src/tools/goals/index.ts +3 -0
  349. package/src/tools/{savePlan.ts → goals/savePlan.ts} +38 -8
  350. package/src/tools/registry.ts +17 -1261
  351. package/src/tools/{checkReleaseGate.ts → release/checkReleaseGate.ts} +3 -3
  352. package/src/tools/release/index.ts +2 -0
  353. package/src/tools/{releaseMode.ts → release/releaseMode.ts} +42 -24
  354. package/src/tools/tasks/cancelTask.ts +110 -0
  355. package/src/tools/{createTask.ts → tasks/createTask.ts} +161 -33
  356. package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +12 -16
  357. package/src/tools/{evidencePack.ts → tasks/evidencePack.ts} +36 -22
  358. package/src/tools/{getTaskFile.ts → tasks/getTaskFile.ts} +4 -4
  359. package/src/tools/{getTaskStatus.ts → tasks/getTaskStatus.ts} +13 -6
  360. package/src/tools/{getTaskStdoutTail.ts → tasks/getTaskStdoutTail.ts} +13 -11
  361. package/src/tools/{getTaskSummary.ts → tasks/getTaskSummary.ts} +46 -44
  362. package/src/tools/tasks/index.ts +18 -0
  363. package/src/tools/{listTasks.ts → tasks/listTasks.ts} +6 -5
  364. package/src/tools/{reconcileTasks.ts → tasks/reconcileTasks.ts} +228 -77
  365. package/src/tools/tasks/retryTask.ts +69 -0
  366. package/src/tools/{runTaskLoop.ts → tasks/runTaskLoop.ts} +73 -46
  367. package/src/tools/{runVerification.ts → tasks/runVerification.ts} +6 -3
  368. package/src/tools/{taskLineage.ts → tasks/taskLineage.ts} +8 -7
  369. package/src/tools/{taskOutputs.ts → tasks/taskOutputs.ts} +27 -20
  370. package/src/tools/tasks/taskStates.ts +54 -0
  371. package/src/tools/{waitForTask.ts → tasks/waitForTask.ts} +3 -12
  372. package/src/tools/{androidDoctor.ts → workspace/androidDoctor.ts} +15 -10
  373. package/src/tools/{applyPatch.ts → workspace/applyPatch.ts} +38 -16
  374. package/src/tools/{discardWorktree.ts → workspace/discardWorktree.ts} +3 -3
  375. package/src/tools/workspace/index.ts +10 -0
  376. package/src/tools/workspace/listAgents.ts +75 -0
  377. package/src/tools/{listWorkspace.ts → workspace/listWorkspace.ts} +3 -3
  378. package/src/tools/{mergeWorktree.ts → workspace/mergeWorktree.ts} +3 -3
  379. package/src/tools/{readWorkspaceFile.ts → workspace/readWorkspaceFile.ts} +8 -8
  380. package/src/tools/{recommendAgentForTask.ts → workspace/recommendAgentForTask.ts} +3 -3
  381. package/src/tools/{searchWorkspace.ts → workspace/searchWorkspace.ts} +5 -5
  382. package/src/tools/workspace/syncFile.ts +292 -0
  383. package/src/utils/atomicFile.ts +74 -0
  384. package/src/utils/boundedFile.ts +116 -0
  385. package/src/utils/lockedJsonFile.ts +251 -0
  386. package/src/utils/platform.ts +31 -0
  387. package/src/utils/stableJson.ts +12 -0
  388. package/src/version.ts +3 -2
  389. package/src/watcherStatus.ts +19 -1
  390. package/ui/desktop-bootstrap.js +11 -0
  391. package/ui/desktop-bridge.js +36 -0
  392. package/ui/desktop.css +160 -0
  393. package/ui/getting-started.js +93 -0
  394. package/ui/i18n.js +484 -0
  395. package/ui/log-parser.js +30 -0
  396. package/ui/pages/audit.html +12 -0
  397. package/ui/pages/dashboard.html +94 -147
  398. package/ui/pages/direct-sessions.html +123 -26
  399. package/ui/pages/getting-started.html +43 -0
  400. package/ui/pages/logs.html +134 -22
  401. package/ui/pages/settings.html +101 -0
  402. package/ui/pages/task-detail.html +12 -0
  403. package/ui/pages/tasks.html +54 -8
  404. package/ui/pages/workspace.html +38 -1
  405. package/ui/settings.js +254 -0
  406. package/PatchWarden-Control.cmd +0 -6
  407. package/Restart-PatchWarden-Control.cmd +0 -6
  408. package/dist/test/unit/acceptance-engine.test.d.ts +0 -1
  409. package/dist/test/unit/acceptance-engine.test.js +0 -228
  410. package/dist/test/unit/agent-router.test.d.ts +0 -1
  411. package/dist/test/unit/agent-router.test.js +0 -287
  412. package/dist/test/unit/android-doctor.test.d.ts +0 -1
  413. package/dist/test/unit/android-doctor.test.js +0 -118
  414. package/dist/test/unit/apply-patch.test.d.ts +0 -1
  415. package/dist/test/unit/apply-patch.test.js +0 -225
  416. package/dist/test/unit/audit-checks.test.d.ts +0 -1
  417. package/dist/test/unit/audit-checks.test.js +0 -350
  418. package/dist/test/unit/chinese-path.test.d.ts +0 -1
  419. package/dist/test/unit/chinese-path.test.js +0 -91
  420. package/dist/test/unit/command-guard.test.d.ts +0 -1
  421. package/dist/test/unit/command-guard.test.js +0 -160
  422. package/dist/test/unit/create-task.test.d.ts +0 -1
  423. package/dist/test/unit/create-task.test.js +0 -197
  424. package/dist/test/unit/diagnose-task.test.d.ts +0 -1
  425. package/dist/test/unit/diagnose-task.test.js +0 -457
  426. package/dist/test/unit/direct-guards.test.d.ts +0 -1
  427. package/dist/test/unit/direct-guards.test.js +0 -328
  428. package/dist/test/unit/discovery-token-store.test.d.ts +0 -1
  429. package/dist/test/unit/discovery-token-store.test.js +0 -139
  430. package/dist/test/unit/evidence-pack.test.d.ts +0 -1
  431. package/dist/test/unit/evidence-pack.test.js +0 -224
  432. package/dist/test/unit/get-task-status.test.d.ts +0 -1
  433. package/dist/test/unit/get-task-status.test.js +0 -174
  434. package/dist/test/unit/get-task-summary.test.d.ts +0 -1
  435. package/dist/test/unit/get-task-summary.test.js +0 -146
  436. package/dist/test/unit/goal-graph.test.d.ts +0 -1
  437. package/dist/test/unit/goal-graph.test.js +0 -298
  438. package/dist/test/unit/goal-progress.test.d.ts +0 -1
  439. package/dist/test/unit/goal-progress.test.js +0 -381
  440. package/dist/test/unit/goal-report.test.d.ts +0 -1
  441. package/dist/test/unit/goal-report.test.js +0 -159
  442. package/dist/test/unit/goal-status.test.d.ts +0 -1
  443. package/dist/test/unit/goal-status.test.js +0 -215
  444. package/dist/test/unit/goal-store.test.d.ts +0 -1
  445. package/dist/test/unit/goal-store.test.js +0 -253
  446. package/dist/test/unit/goal-subgoal-task.test.d.ts +0 -1
  447. package/dist/test/unit/goal-subgoal-task.test.js +0 -55
  448. package/dist/test/unit/goal-tools-registry.test.d.ts +0 -1
  449. package/dist/test/unit/goal-tools-registry.test.js +0 -192
  450. package/dist/test/unit/handoff-export.test.d.ts +0 -1
  451. package/dist/test/unit/handoff-export.test.js +0 -263
  452. package/dist/test/unit/invoke-discovered-tool.test.d.ts +0 -1
  453. package/dist/test/unit/invoke-discovered-tool.test.js +0 -167
  454. package/dist/test/unit/logging.test.d.ts +0 -1
  455. package/dist/test/unit/logging.test.js +0 -397
  456. package/dist/test/unit/path-guard.test.d.ts +0 -1
  457. package/dist/test/unit/path-guard.test.js +0 -133
  458. package/dist/test/unit/post-task-cleanup.test.d.ts +0 -1
  459. package/dist/test/unit/post-task-cleanup.test.js +0 -48
  460. package/dist/test/unit/project-policy-release-mode.test.d.ts +0 -1
  461. package/dist/test/unit/project-policy-release-mode.test.js +0 -125
  462. package/dist/test/unit/reconcile-tasks.test.d.ts +0 -1
  463. package/dist/test/unit/reconcile-tasks.test.js +0 -456
  464. package/dist/test/unit/release-gate.test.d.ts +0 -1
  465. package/dist/test/unit/release-gate.test.js +0 -242
  466. package/dist/test/unit/run-task-loop.test.d.ts +0 -1
  467. package/dist/test/unit/run-task-loop.test.js +0 -380
  468. package/dist/test/unit/safe-status.test.d.ts +0 -1
  469. package/dist/test/unit/safe-status.test.js +0 -165
  470. package/dist/test/unit/safe-views.test.d.ts +0 -1
  471. package/dist/test/unit/safe-views.test.js +0 -171
  472. package/dist/test/unit/schema-drift-check.test.d.ts +0 -1
  473. package/dist/test/unit/schema-drift-check.test.js +0 -176
  474. package/dist/test/unit/sensitive-guard.test.d.ts +0 -1
  475. package/dist/test/unit/sensitive-guard.test.js +0 -104
  476. package/dist/test/unit/spec-kit-import.test.d.ts +0 -1
  477. package/dist/test/unit/spec-kit-import.test.js +0 -341
  478. package/dist/test/unit/subgoal-sync.test.d.ts +0 -1
  479. package/dist/test/unit/subgoal-sync.test.js +0 -183
  480. package/dist/test/unit/sync-file.test.d.ts +0 -1
  481. package/dist/test/unit/sync-file.test.js +0 -154
  482. package/dist/test/unit/tool-invocation-guard.test.d.ts +0 -1
  483. package/dist/test/unit/tool-invocation-guard.test.js +0 -432
  484. package/dist/test/unit/tool-usage-stats.test.d.ts +0 -1
  485. package/dist/test/unit/tool-usage-stats.test.js +0 -300
  486. package/dist/test/unit/toolSearch.test.d.ts +0 -1
  487. package/dist/test/unit/toolSearch.test.js +0 -571
  488. package/dist/test/unit/wait-for-task.test.d.ts +0 -1
  489. package/dist/test/unit/wait-for-task.test.js +0 -144
  490. package/dist/test/unit/watcher-status.test.d.ts +0 -1
  491. package/dist/test/unit/watcher-status.test.js +0 -169
  492. package/dist/test/unit/worktree-manager.test.d.ts +0 -1
  493. package/dist/test/unit/worktree-manager.test.js +0 -176
  494. package/dist/tools/applyPatch.js +0 -41
  495. package/dist/tools/cancelTask.d.ts +0 -30
  496. package/dist/tools/cancelTask.js +0 -64
  497. package/dist/tools/finalizeDirectSession.js +0 -84
  498. package/dist/tools/goalSubgoalTask.js +0 -110
  499. package/dist/tools/killTask.d.ts +0 -15
  500. package/dist/tools/listAgents.d.ts +0 -12
  501. package/dist/tools/listAgents.js +0 -42
  502. package/dist/tools/reconcileTasks.js +0 -352
  503. package/dist/tools/retryTask.js +0 -45
  504. package/dist/tools/runVerification.js +0 -36
  505. package/dist/tools/syncFile.js +0 -65
  506. package/docs/control-center/control-center-daily-driver.md +0 -211
  507. package/docs/control-center/control-center-mvp.md +0 -205
  508. package/docs/control-center/control-center-phase2.md +0 -159
  509. package/docs/execution-plan-2026-07-09.md +0 -315
  510. package/docs/release-v0.6.0.md +0 -71
  511. package/docs/release-v0.6.1.md +0 -75
  512. package/docs/release-v0.6.4.md +0 -45
  513. package/docs/roadmap-execution-and-acceptance.md +0 -365
  514. package/src/test/unit/acceptance-engine.test.ts +0 -261
  515. package/src/test/unit/agent-router.test.ts +0 -342
  516. package/src/test/unit/android-doctor.test.ts +0 -158
  517. package/src/test/unit/apply-patch.test.ts +0 -293
  518. package/src/test/unit/audit-checks.test.ts +0 -567
  519. package/src/test/unit/chinese-path.test.ts +0 -106
  520. package/src/test/unit/command-guard.test.ts +0 -221
  521. package/src/test/unit/create-task.test.ts +0 -255
  522. package/src/test/unit/diagnose-task.test.ts +0 -544
  523. package/src/test/unit/direct-guards.test.ts +0 -467
  524. package/src/test/unit/discovery-token-store.test.ts +0 -181
  525. package/src/test/unit/evidence-pack.test.ts +0 -251
  526. package/src/test/unit/get-task-status.test.ts +0 -203
  527. package/src/test/unit/get-task-summary.test.ts +0 -173
  528. package/src/test/unit/goal-graph.test.ts +0 -347
  529. package/src/test/unit/goal-progress.test.ts +0 -538
  530. package/src/test/unit/goal-report.test.ts +0 -189
  531. package/src/test/unit/goal-status.test.ts +0 -270
  532. package/src/test/unit/goal-store.test.ts +0 -318
  533. package/src/test/unit/goal-subgoal-task.test.ts +0 -72
  534. package/src/test/unit/goal-tools-registry.test.ts +0 -245
  535. package/src/test/unit/handoff-export.test.ts +0 -295
  536. package/src/test/unit/invoke-discovered-tool.test.ts +0 -216
  537. package/src/test/unit/logging.test.ts +0 -497
  538. package/src/test/unit/path-guard.test.ts +0 -180
  539. package/src/test/unit/post-task-cleanup.test.ts +0 -53
  540. package/src/test/unit/project-policy-release-mode.test.ts +0 -156
  541. package/src/test/unit/reconcile-tasks.test.ts +0 -551
  542. package/src/test/unit/release-gate.test.ts +0 -314
  543. package/src/test/unit/run-task-loop.test.ts +0 -425
  544. package/src/test/unit/safe-status.test.ts +0 -187
  545. package/src/test/unit/safe-views.test.ts +0 -184
  546. package/src/test/unit/schema-drift-check.test.ts +0 -260
  547. package/src/test/unit/sensitive-guard.test.ts +0 -124
  548. package/src/test/unit/spec-kit-import.test.ts +0 -429
  549. package/src/test/unit/subgoal-sync.test.ts +0 -236
  550. package/src/test/unit/sync-file.test.ts +0 -231
  551. package/src/test/unit/tool-invocation-guard.test.ts +0 -542
  552. package/src/test/unit/tool-usage-stats.test.ts +0 -384
  553. package/src/test/unit/toolSearch.test.ts +0 -756
  554. package/src/test/unit/wait-for-task.test.ts +0 -176
  555. package/src/test/unit/watcher-status.test.ts +0 -190
  556. package/src/test/unit/worktree-manager.test.ts +0 -247
  557. package/src/tools/cancelTask.ts +0 -70
  558. package/src/tools/listAgents.ts +0 -54
  559. package/src/tools/retryTask.ts +0 -53
  560. package/src/tools/syncFile.ts +0 -122
  561. /package/dist/tools/{toolCatalog.d.ts → catalog/toolCatalog.d.ts} +0 -0
  562. /package/dist/tools/{toolSearch.d.ts → catalog/toolSearch.d.ts} +0 -0
  563. /package/dist/tools/{toolSearch.js → catalog/toolSearch.js} +0 -0
  564. /package/dist/tools/{toolUsageStats.d.ts → catalog/toolUsageStats.d.ts} +0 -0
  565. /package/dist/tools/{toolUsageStats.js → catalog/toolUsageStats.js} +0 -0
  566. /package/dist/tools/{createDirectSession.d.ts → direct/createDirectSession.d.ts} +0 -0
  567. /package/dist/tools/{runDirectVerificationBundle.d.ts → direct/runDirectVerificationBundle.d.ts} +0 -0
  568. /package/dist/tools/{getPlan.d.ts → goals/getPlan.d.ts} +0 -0
  569. /package/dist/tools/{savePlan.d.ts → goals/savePlan.d.ts} +0 -0
  570. /package/dist/tools/{evidencePack.d.ts → tasks/evidencePack.d.ts} +0 -0
  571. /package/dist/tools/{getTaskFile.d.ts → tasks/getTaskFile.d.ts} +0 -0
  572. /package/dist/tools/{getTaskProgress.d.ts → tasks/getTaskProgress.d.ts} +0 -0
  573. /package/dist/tools/{getTaskProgress.js → tasks/getTaskProgress.js} +0 -0
  574. /package/dist/tools/{getTaskStdoutTail.d.ts → tasks/getTaskStdoutTail.d.ts} +0 -0
  575. /package/dist/tools/{getTaskSummary.d.ts → tasks/getTaskSummary.d.ts} +0 -0
  576. /package/dist/tools/{killTask.js → tasks/killTask.js} +0 -0
  577. /package/dist/tools/{retryTask.d.ts → tasks/retryTask.d.ts} +0 -0
  578. /package/dist/tools/{runVerification.d.ts → tasks/runVerification.d.ts} +0 -0
  579. /package/dist/tools/{taskLineage.d.ts → tasks/taskLineage.d.ts} +0 -0
  580. /package/dist/tools/{waitForTask.d.ts → tasks/waitForTask.d.ts} +0 -0
  581. /package/dist/tools/{androidDoctor.d.ts → workspace/androidDoctor.d.ts} +0 -0
  582. /package/dist/tools/{discardWorktree.d.ts → workspace/discardWorktree.d.ts} +0 -0
  583. /package/dist/tools/{listWorkspace.d.ts → workspace/listWorkspace.d.ts} +0 -0
  584. /package/dist/tools/{mergeWorktree.d.ts → workspace/mergeWorktree.d.ts} +0 -0
  585. /package/dist/tools/{readWorkspaceFile.d.ts → workspace/readWorkspaceFile.d.ts} +0 -0
  586. /package/dist/tools/{recommendAgentForTask.d.ts → workspace/recommendAgentForTask.d.ts} +0 -0
  587. /package/dist/tools/{searchWorkspace.d.ts → workspace/searchWorkspace.d.ts} +0 -0
  588. /package/src/tools/{toolSearch.ts → catalog/toolSearch.ts} +0 -0
  589. /package/src/tools/{toolUsageStats.ts → catalog/toolUsageStats.ts} +0 -0
  590. /package/src/tools/{getTaskProgress.ts → tasks/getTaskProgress.ts} +0 -0
  591. /package/src/tools/{killTask.ts → tasks/killTask.ts} +0 -0
package/README.md CHANGED
@@ -8,20 +8,31 @@
8
8
  [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18-339933.svg)](https://nodejs.org/)
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
10
10
 
11
- 当前源码版本:**v1.5.1**。查看
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.2](https://github.com/jiezeng2004-design/PatchWarden/releases/download/v1.6.2/PatchWarden-Setup-1.6.2-x64.exe)
19
+ · [免安装 ZIP](https://github.com/jiezeng2004-design/PatchWarden/releases/download/v1.6.2/PatchWarden-Portable-1.6.2-x64.zip)
20
+ · [校验文件](https://github.com/jiezeng2004-design/PatchWarden/releases/download/v1.6.2/PatchWarden-Desktop-SHA256SUMS.txt)
21
+ · [三分钟快速开始](#三分钟快速开始)
22
+ · [Discussions](https://github.com/jiezeng2004-design/PatchWarden/discussions)
23
+
24
+ ![PatchWarden Desktop 真实首启界面](docs/assets/patchwarden-desktop-onboarding.png)
21
25
 
22
- > PatchWarden is a local-first MCP safety and verification layer for AI coding agents, with workspace confinement, command allowlists, scope-violation detection, and auditable task evidence.
26
+ <sub>PatchWarden Desktop 真实首启界面,来自隐私安全的桌面 smoke 验收;截图未使用真实工作区、账号或凭据。</sub>
27
+
28
+ 当前源码版本和 Windows Release:**v1.6.2**。npm `latest` 由维护者独立手动发布,
29
+ 请以页面顶部的 npm 徽章和注册表查询结果为准。Windows 首次体验推荐上面的安装版;查看
30
+ [CHANGELOG](CHANGELOG.md)、[迁移指南](docs/migration-from-safe-bifrost.md)和
31
+ [发布检查清单](docs/release-checklist.md)。
23
32
 
24
- ![PatchWarden ChatGPT connector demo](docs/assets/patchwarden-chatgpt-demo.svg)
33
+ > [!NOTE]
34
+ > 当前桌面安装包暂时只提供 Windows x64。macOS、Linux 或暂时不安装桌面版的用户,
35
+ > 可以继续使用 npm 上已有的稳定版本及其内置本地 Dashboard;本次 GitHub Release 不自动执行 `npm publish`。
25
36
 
26
37
  > [!IMPORTANT]
27
38
  > PatchWarden 不是通用远程 Shell。MCP 客户端不能随意执行命令:
@@ -33,7 +44,7 @@ PatchWarden 负责把计划保存成工作区内任务,再由预先配置的
33
44
  - [它解决什么问题](#它解决什么问题)
34
45
  - [运行结构](#运行结构)
35
46
  - [环境要求](#环境要求)
36
- - [五分钟快速开始](#五分钟快速开始)
47
+ - [三分钟快速开始](#三分钟快速开始)
37
48
  - [完整配置说明](#完整配置说明)
38
49
  - [接入 OpenCode](#接入-opencode)
39
50
  - [接入 Codex](#接入-codex)
@@ -58,9 +69,9 @@ PatchWarden 负责把计划保存成工作区内任务,再由预先配置的
58
69
  - 每个任务必须指定位于 `workspaceRoot` 内的 `repo_path`。
59
70
  - Agent 启动命令来自本地配置,而不是来自模型输入。
60
71
  - 测试命令必须精确匹配 `allowedTestCommands`。
61
- - 任务完成后保存结构化结果、完整差异、文件统计和独立验证记录。
72
+ - 任务完成后保存结构化结果、有界且脱敏的差异、文件统计和独立验证记录。
62
73
  - 工作区外出现变化时,任务会标记为作用域违规,而不是悄悄接受。
63
- - `.env`、Token、SSH 密钥、Cookie、凭据文件等敏感路径默认不可读。
74
+ - `.patchwarden/` 不是敏感路径豁免区;`.env`、Token、SSH 密钥、Cookie、凭据文件等敏感名称无论位于哪一层目录都不可读。
64
75
 
65
76
  适合的场景:
66
77
 
@@ -134,9 +145,32 @@ where.exe codex
134
145
  应用程序,它不一定是可供 PatchWarden 调用的 Codex CLI。请安装或指定真正的
135
146
  CLI,或者先把 OpenCode 配置为执行 Agent。
136
147
 
137
- ## 五分钟快速开始
148
+ ## 三分钟快速开始
149
+
150
+ 如果已经安装 Node.js 18+ 和至少一个本地编程 Agent,这条 Windows 路径的目标是
151
+ 在三分钟内完成首次只读健康检查。首次体验不需要配置 ChatGPT Tunnel。
152
+
153
+ ### 方案 A:Windows 安装版(首次体验推荐)
154
+
155
+ 1. 下载 [Windows 安装版 v1.6.2](https://github.com/jiezeng2004-design/PatchWarden/releases/download/v1.6.2/PatchWarden-Setup-1.6.2-x64.exe)
156
+ 和 [SHA256 校验文件](https://github.com/jiezeng2004-design/PatchWarden/releases/download/v1.6.2/PatchWarden-Desktop-SHA256SUMS.txt)。
157
+ 2. 在 PowerShell 中校验安装包:
138
158
 
139
- ### 方案 A:从源码运行(推荐)
159
+ ```powershell
160
+ Get-FileHash .\PatchWarden-Setup-1.6.2-x64.exe -Algorithm SHA256
161
+ ```
162
+
163
+ 请以同一 Release 的校验文件为准。安装包尚未代码签名,Windows SmartScreen
164
+ 可能显示“未知发布者”。
165
+
166
+ 3. 安装并打开 PatchWarden Desktop,选择一个只包含项目的专用工作区。
167
+ 4. 首屏选择 **本地 MCP**,确认检测到的 Agent,让只读健康检查完成。
168
+
169
+ 成功标志:向导能够进入只读控制台,显示所选工作区,并完成 Agent 与运行环境检查。
170
+ 安装成功或遇到阻塞时,请到 [Discussions](https://github.com/jiezeng2004-design/PatchWarden/discussions)
171
+ 记录操作系统、Node.js 版本、使用的 Agent 和卡住的步骤。
172
+
173
+ ### 方案 B:从源码运行(开发者)
140
174
 
141
175
  源码方式最适合完整使用 Watcher、Windows 一键启动器和诊断脚本。
142
176
 
@@ -171,7 +205,7 @@ npm.cmd run watch
171
205
 
172
206
  保持该窗口运行,再按照后文配置 OpenCode、Codex 或 ChatGPT Connector。
173
207
 
174
- ### 方案 B:使用 npm 包
208
+ ### 方案 C:使用 npm 包
175
209
 
176
210
  npm 包适合让 MCP 客户端通过固定版本启动 PatchWarden。为了执行任务,
177
211
  仍然必须另外启动 Watcher。
@@ -213,11 +247,13 @@ Copy-Item .\examples\config.example.json .\patchwarden.config.json
213
247
  "agents": {
214
248
  "opencode": {
215
249
  "command": "opencode",
216
- "args": ["run", "{prompt}"]
250
+ "args": ["run", "{prompt}"],
251
+ "envAllowlist": []
217
252
  },
218
253
  "codex": {
219
254
  "command": "codex",
220
- "args": ["exec", "--cd", "{repo}", "{prompt}"]
255
+ "args": ["exec", "--cd", "{repo}", "{prompt}"],
256
+ "envAllowlist": []
221
257
  }
222
258
  },
223
259
  "allowedTestCommands": [
@@ -246,6 +282,7 @@ Copy-Item .\examples\config.example.json .\patchwarden.config.json
246
282
  | `tasksDir` | 是 | 任务和结果目录,通常使用 `.patchwarden/tasks`。 |
247
283
  | `toolProfile` | 否 | `full`、`chatgpt_core`、`chatgpt_direct` 或 `chatgpt_search`;本地客户端推荐 `full`,动态工具发现场景使用 `chatgpt_search`。 |
248
284
  | `agents` | 是 | 可执行 Agent 白名单;支持 `{repo}` 和 `{prompt}` 占位符。 |
285
+ | `agents.<name>.envAllowlist` | 否 | 显式传给该 Agent 的 provider 环境变量名;默认不继承。Tunnel owner token 禁止透传。 |
249
286
  | `allowedTestCommands` | 是 | 独立验证命令白名单,调用时必须精确匹配。 |
250
287
  | `repoAllowedTestCommands` | 否 | 按工作区相对仓库路径增加精确验证命令;不支持通配符。 |
251
288
  | `maxReadFileBytes` | 是 | MCP 单次文件读取上限。 |
@@ -369,6 +406,21 @@ PATCHWARDEN_TOOL_PROFILE = "full"
369
406
 
370
407
  ## 接入 ChatGPT Connector
371
408
 
409
+ > 当前 ChatGPT 术语为 **developer-mode app / Plugins**;旧版文档与部分界面曾称为 Connector。新配置请以 Plugins 为准。
410
+
411
+ ### 桌面版八步新手流程
412
+
413
+ 1. 确认 OpenAI Platform 账号具有 Tunnel 权限,并在目标 ChatGPT workspace 开启 developer mode。
414
+ 2. 打开 [Platform Tunnel 设置](https://platform.openai.com/settings/organization/tunnels),创建 Tunnel,并关联目标 ChatGPT workspace。
415
+ 3. 启动 PatchWarden Desktop,在“设置 → MCP 与隧道”检测或选择现有的 `tunnel-client.exe`;应用不会自动下载或执行新软件。
416
+ 4. 输入 Core Tunnel ID 与专用 Tunnel runtime API key。该凭据对应 `CONTROL_PLANE_API_KEY`,**不是**普通应用使用的 `OPENAI_API_KEY`。
417
+ 5. 配置环境代理、无代理或不含凭据的 HTTP/HTTPS/SOCKS5(Mixed)代理,并先确认代理端口可达。
418
+ 6. 点击“配置并验证 Core”。桌面端初始化 `patchwarden` profile,运行 `tunnel-client doctor --explain --json`,验证成功后才用 Windows DPAPI 保存凭据;随后启动 Core。
419
+ 7. 在“开始使用”确认 Tunnel ready、Watcher healthy,并确认 `chatgpt_core` 发现固定的 26 个工具。Direct 是设置页中的可选高级能力,未启用时“全部启动”只启动 Core。
420
+ 8. 在 ChatGPT **Settings → Plugins** 创建 developer-mode app,选择刚创建的 Tunnel;重新连接后新建对话,调用 `health_check` 验证。
421
+
422
+ 首次引导也可选择“本地 MCP”。该路线只配置安全工作区和本地 MCP 客户端,可以跳过第 1、2、4、5、7、8 步中的 Platform/ChatGPT Tunnel 操作。
423
+
372
424
  推荐的 Windows 链路:
373
425
 
374
426
  ```text
@@ -516,7 +568,7 @@ Tunnel。支持范围可能变化,因此不要在项目里写死国家列表
516
568
  推荐顺序:
517
569
 
518
570
  1. `health_check`:确认版本、工作区、Watcher 和工具目录。
519
- 2. `list_agents`:确认本地 Agent 命令可用。
571
+ 2. `list_agents`:确认本地 Agent 可执行文件可用;该检查不会验证模型提供商余额、登录态或调用权限,结果中的 `provider_status` 默认为 `not_checked`。
520
572
  3. `list_workspace`:确定 `repo_path`。
521
573
  4. `save_plan`,或在创建任务时提供 `inline_plan`。
522
574
  5. `create_task`:明确 Agent、仓库和验证命令。
@@ -572,19 +624,25 @@ ChatGPT 任务应优先选择前三个守护模板:只读诊断使用 `inspect
572
624
  | `progress.md` | Agent 写入的进度记录。 |
573
625
  | `result.md` | 人类可读的执行报告。 |
574
626
  | `result.json` | 结构化结果、路径、变更、警告和后续建议。 |
575
- | `diff.patch` | 完整任务差异证据。 |
627
+ | `diff.patch` | 最多 20 MiB 的任务差异证据;疑似凭据会在落盘前脱敏,超限会明确标记截断。 |
576
628
  | `artifact_manifest.json` | 构建或发布产物的路径、类型、大小与 SHA-256。 |
577
629
  | `file-stats.json` | 文件级增删统计。 |
578
630
  | `verify.json` | 每条独立验证命令的结构化记录。 |
579
631
  | `verify.log` | 独立验证的可读日志。 |
580
632
  | `test.log` | Agent 执行过程中产生的测试输出。 |
581
633
 
634
+ 面向客户端的任务产物读取受 `maxReadFileBytes` 限制;diff、summary 与日志 tail
635
+ 只读取有界前缀或尾部。`audit_task` 最多扫描 200 个 Markdown 文件和 4 MiB 文档内容,
636
+ 达到预算会返回 warning。invocation/reconcile 等持续日志使用跨进程锁内的有界追加,
637
+ 超限时保留最近内容并写入明确的截断标记。
638
+
582
639
  本地 Agent 声称“已推送”“已发布”不属于可靠的远程证据。GitHub、npm、
583
640
  Tag 和 Release 必须再用对应平台的实时状态核验。
584
641
 
585
642
  ## HTTP MCP 模式
586
643
 
587
- HTTP Server 只绑定 `127.0.0.1`,默认端口 7331,不会直接监听局域网。
644
+ HTTP Server 只绑定 `127.0.0.1`,默认端口 7331,不会直接监听局域网;
645
+ HTTP MCP 与 Control Center 同时拒绝非 `127.0.0.1`/`localhost` 的 Host,防止 DNS rebinding 绕过回环边界。
588
646
 
589
647
  终端 1,启动 Watcher:
590
648
 
@@ -659,10 +717,43 @@ Direct 两种模式的启动、停止、重启与状态。也可以在 PowerShel
659
717
  .\PatchWarden.cmd restart all
660
718
  .\PatchWarden.cmd status all
661
719
  .\PatchWarden.cmd kill all
662
- .\Stop-PatchWarden.cmd
720
+ .\scripts\launchers\Stop-PatchWarden.cmd
663
721
  ```
664
722
 
665
- 日常桌面入口:`PatchWarden-Desktop.cmd` 启动托盘并确保 Control Center 可用,不自动打开额外浏览器窗口。只有调试托盘时才使用 `PatchWarden-Control-Tray.cmd --foreground`;需要完整 Web 控制台时打开 `PatchWarden-Control.cmd`;需要一键收尾时使用 `Stop-PatchWarden.cmd` 关闭 Core/Direct、Control Center 和托盘。
723
+ 日常桌面入口:`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 和托盘。
724
+
725
+ ### Windows 安装版
726
+
727
+ 公开分发可以构建 PatchWarden Desktop 安装包和免安装 ZIP。它提供 Electron
728
+ 独立窗口、首次工作区引导、系统托盘和设置页,同时继续复用相同的
729
+ loopback Control Center 与安全控制 API。桌面依赖隔离在私有的
730
+ `desktop/` 子包中,不进入 `patchwarden` npm 包。
731
+
732
+ 安装版仍要求 Windows x64、Node.js 18+ 和 tunnel-client。首次启动会在
733
+ `PATH`、当前用户目录和工作区附近做有界检测;未找到时可进入只读控制台,
734
+ 再从“设置 -> MCP 与隧道”选择 `tunnel-client.exe` 并查看 SHA256 校验步骤。
735
+ 应用不会自动下载或运行新软件。设置页还提供 Direct 显式开关,以及环境代理、
736
+ 无代理、手动代理三种模式;手动代理只接受不含凭据的 http/https/socks5 URL。
737
+ 启动和重启只有在 health/ready 与 Core Watcher 验证通过后才会报告成功。
738
+ 桌面端支持 Codex、OpenCode、Claude Code、Gemini CLI、GitHub Copilot CLI、
739
+ Qwen Code、Kimi Code 和 Aider;只读取各 Agent 配置中的模型字段,不读取凭据。
740
+
741
+ ```powershell
742
+ npm.cmd install --prefix desktop --cache .\.npm-cache
743
+ npm.cmd run desktop:test
744
+ npm.cmd run desktop:preflight
745
+ npm.cmd run desktop:package
746
+ ```
747
+
748
+ 安装包、免安装 ZIP 与 SHA256 文件输出到 `release\desktop`。首版不包含自动更新或
749
+ 代码签名,Windows SmartScreen 可能提示未知发布者;发布前必须核对
750
+ GitHub Release 来源和 SHA256。完整安装、运行与卸载说明见
751
+ [docs/desktop-app.md](docs/desktop-app.md)。
752
+
753
+ `desktop:preflight` 会使用唯一目录完成 clean build、完整单测、桌面测试、npm 包面、
754
+ Electron 目录包、仓库外 26-tool manifest 与隔离 unpacked UI/单实例验收,并生成
755
+ `preflight-report.json` / `preflight-report.md`。正式发布前必须在干净 checkout 运行
756
+ `npm.cmd run desktop:preflight:release`;该命令发现未提交路径时会直接阻断。
666
757
 
667
758
  旧的单用途入口保留在 `scripts/launchers/` 作为兼容层;个人入口位于
668
759
  `.local/launchers/`,并继续被 Git 和发布包排除。`stop` / `restart` 会同时检查
@@ -758,7 +849,7 @@ PatchWarden Dashboard (Control Center) 提供以下主要页面:
758
849
 
759
850
  - **控制台 (Dashboard)**: 系统总览,包含 Repo selector、Health Score、服务状态、Release 卡片、Project Policy、Lineage、Evidence Pack、Stale 任务提示、最近任务列表、系统状态(含 Copy diagnostics)。
760
851
  - **任务面板 (Tasks)**: 任务列表,支持按 repo_path / status / acceptance_status / warning type / agent / date range 过滤,每行提供 safe_result / safe_audit / safe_test_summary / safe_diff_summary 快捷操作。
761
- - **任务详情 (Task Detail)**: 默认展示 safe 摘要(safe_result / safe_test_summary / safe_diff_summary / safe_audit),完整 result / diff / test log 在折叠的高级区按需加载。
852
+ - **任务详情 (Task Detail)**: 默认展示 safe 摘要(safe_result / safe_test_summary / safe_diff_summary / safe_audit),配置上限内的 result / diff / test log 在折叠的高级区按需加载。
762
853
  - **Direct 会话 (Direct Sessions)**: 按 active / finalized / audited / expired 分组,提供 safe_direct_summary / safe_finalize_direct_session / safe_audit_direct_session 快捷操作。
763
854
  - **审计日志 / Warnings (Audit)**: 按 warning 类型聚合,显示 affected tasks / severity / recommended action。
764
855
  - **工作区 (Workspace)**: workspace 一级目录与项目列表。
@@ -769,7 +860,7 @@ PatchWarden Dashboard (Control Center) 提供以下主要页面:
769
860
  1. 在 Dashboard 顶部选择目标 repo。
770
861
  2. 查看 Health Score 确认系统健康。
771
862
  3. 在最近任务列表点击 safe_result 快捷查看任务摘要。
772
- 4. 打开 Task Detail 进行 safe-first 验收,仅在需要时展开高级区查看完整日志。
863
+ 4. 打开 Task Detail 进行 safe-first 验收,仅在需要时展开高级区查看有界日志。
773
864
  5. 使用 Lineage Detail 查看 run_task_loop 的成功/失败原因。
774
865
  6. 验收完成后导出 Evidence Pack。
775
866
  7. Direct 会话用于独立验证,完成后 finalize + audit。
@@ -894,6 +985,12 @@ creation. `export_task_evidence_pack` writes bounded `evidence.json` and
894
985
  `EVIDENCE.md` files under `.patchwarden/evidence-packs/<lineage_id>/` without
895
986
  stdout/stderr tails, full diffs, verification logs, or sensitive file content.
896
987
 
988
+ Goal Session 的状态变更通过共享的跨进程 mutation lock 串行化并原子落盘;
989
+ 非空 Goal 的所有子目标都被 `accept_subgoal` 接受后,Goal 会自动转为 `completed`。
990
+ `create_subgoal_task` 以 Goal 已保存的 `repo_path` 为权威,调用方传入其他仓库会以
991
+ `goal_repo_mismatch` 拒绝;隔离 worktree 也从该仓库创建,且 create/merge/discard
992
+ 共用仓库级 lifecycle lock。
993
+
897
994
  `get_project_policy` 是 v1.3 的只读策略入口,返回 `.patchwarden/project-policy.json`
898
995
  的有界 effective policy 与 release readiness,不会扩大命令白名单。v1.3 的
899
996
  `release_check`、`release_prepare`、`release_verify`、`release_cleanup` 仅在 `full`
@@ -997,6 +1094,9 @@ Direct 模式在受控 session 内执行,具有以下硬性限制:
997
1094
  - 禁止修改 `node_modules`
998
1095
  - 禁止手动修改 release/dist
999
1096
  - 禁止修改二进制文件
1097
+ - 只修改大小受限的 UTF-8 文本;补丁按 UTF-8 字节计数
1098
+ - 补丁或同步结果包含疑似凭据时拒绝写入
1099
+ - 同一 session 的 patch、sync、verify、finalize、audit 串行化
1000
1100
  - 所有操作限制在 `workspaceRoot` 和 session repo 内
1001
1101
 
1002
1102
  ### 不支持内容
@@ -1022,9 +1122,9 @@ PatchWarden 的主要保护:
1022
1122
  - Agent 命令和参数模板必须预先配置。
1023
1123
  - 验证命令必须精确匹配白名单。
1024
1124
  - 文件访问被限制在 `workspaceRoot` 内。
1025
- - 敏感文件名和明显的凭据读取计划会被阻止。
1026
- - 任务产物中的疑似密钥值会被脱敏。
1027
- - HTTP Server 只绑定 `127.0.0.1`。
1125
+ - 敏感文件名和明显的凭据读取计划会被阻止;`.patchwarden/` 不会绕过该检查。
1126
+ - 任务差异中的疑似密钥值会在落盘前脱敏,并把任务标记为策略违规。
1127
+ - HTTP 服务只绑定 `127.0.0.1`,并拒绝非回环 Host。
1028
1128
  - Runner 不会自动 commit、push、发布或重置仓库。
1029
1129
 
1030
1130
  需要保护的本地路径:
@@ -1,8 +1,9 @@
1
- import { writeFileSync } from "node:fs";
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
8
  export async function runAgentAssessment(input) {
8
9
  const logPaths = {
@@ -26,9 +27,11 @@ export async 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 assessmentPrompt = buildAssessmentPrompt(input.goal, input.planContent, input.repoPath);
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
- writeFileSync(promptFilePath, assessmentPrompt, "utf-8");
34
+ atomicWriteFileSync(promptFilePath, assessmentPrompt);
32
35
  logPaths.prompt = promptFilePath;
33
36
  // ── 2. Before snapshot (repo-scoped) ──
34
37
  let repoBefore;
@@ -68,6 +71,8 @@ export async 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;
@@ -101,11 +106,11 @@ export async 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
- writeFileSync(summary.log_paths.violation, JSON.stringify({
109
+ atomicWriteJsonFileSync(summary.log_paths.violation, {
105
110
  violation: "read_only_violation",
106
111
  files: violationFiles,
107
112
  exit_code: result.exitCode,
108
- }, null, 2), "utf-8");
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 parsed;
224
+ let parsedValue;
220
225
  try {
221
- parsed = JSON.parse(jsonText.slice(jsonStart, jsonEnd + 1));
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((r) => typeof r === "string" && r.length <= 100);
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((d) => typeof d === "string" && d.length <= 200);
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
- writeFileSync(path, JSON.stringify(summary, null, 2), "utf-8");
301
+ atomicWriteJsonFileSync(path, summary);
293
302
  }
@@ -0,0 +1,29 @@
1
+ import type { AssessmentValidationResult } from "./assessmentStore.js";
2
+ export interface AssessmentValidationFailureDiagnostic {
3
+ diagnostic_version: string;
4
+ error_code: string;
5
+ reason_code: string;
6
+ assessment_id: string | null;
7
+ assessment_config_sha256: string | null;
8
+ current_config_sha256: string | null;
9
+ assessment_config_hash: string | null;
10
+ current_config_hash: string | null;
11
+ snapshot_version: string | null;
12
+ assessment_fingerprint_version: string | null;
13
+ current_fingerprint_version: string;
14
+ assessment_schema_epoch: string | null;
15
+ current_schema_epoch: string;
16
+ assessment_created_at: string | null;
17
+ assessment_expires_at: string | null;
18
+ config_changed: boolean;
19
+ changed_field_names: string[];
20
+ changed_config_sections: string[];
21
+ validator_module_path: string;
22
+ validator_build_id: string;
23
+ watcher_instance_id: string | null;
24
+ observed_at: string;
25
+ }
26
+ export declare function buildAssessmentValidationFailureDiagnostic(result: AssessmentValidationResult): AssessmentValidationFailureDiagnostic | null;
27
+ /** Persist a bounded value-free failure summary without changing validation behavior. */
28
+ export declare function recordAssessmentValidationFailure(result: AssessmentValidationResult): boolean;
29
+ export declare function readLastAssessmentValidationFailure(): AssessmentValidationFailureDiagnostic | null;
@@ -0,0 +1,132 @@
1
+ import { existsSync, mkdirSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { getAssessmentsDir, getConfig } from "../config.js";
4
+ import { guardPath } from "../security/pathGuard.js";
5
+ import { atomicWriteJsonFileSync } from "../utils/atomicFile.js";
6
+ import { readJsonObjectFileSync } from "../utils/lockedJsonFile.js";
7
+ import { ASSESSMENT_SECURITY_SNAPSHOT_VERSION } from "./securitySnapshot.js";
8
+ import { PATCHWARDEN_VERSION, TOOL_SCHEMA_EPOCH } from "../version.js";
9
+ const DIAGNOSTIC_VERSION = "assessment-validation-diagnostic-v2";
10
+ const DIAGNOSTIC_FILE = "last-validation-failure.json";
11
+ const VALIDATOR_MODULE_PATH = "dist/assessments/assessmentStore.js";
12
+ const VALIDATOR_BUILD_ID = `patchwarden-${PATCHWARDEN_VERSION}:${TOOL_SCHEMA_EPOCH}:${ASSESSMENT_SECURITY_SNAPSHOT_VERSION}`;
13
+ export function buildAssessmentValidationFailureDiagnostic(result) {
14
+ if (result.valid || !result.failure_reason)
15
+ return null;
16
+ const reasonCode = safeCode(result.failure_reason);
17
+ const assessmentHash = safeHash(result.expected_hash);
18
+ const currentHash = safeHash(result.actual_hash);
19
+ const changedSections = [...new Set((result.config_change_categories || [])
20
+ .map((name) => safeCode(name))
21
+ .filter(Boolean))]
22
+ .sort()
23
+ .slice(0, 32);
24
+ const assessmentVersion = safeText(result.assessment?.assessment_security_snapshot_version, 80);
25
+ return {
26
+ diagnostic_version: DIAGNOSTIC_VERSION,
27
+ error_code: reasonCode,
28
+ reason_code: reasonCode,
29
+ assessment_id: safeAssessmentId(result.assessment?.assessment_id),
30
+ assessment_config_sha256: assessmentHash,
31
+ current_config_sha256: currentHash,
32
+ assessment_config_hash: assessmentHash,
33
+ current_config_hash: currentHash,
34
+ snapshot_version: assessmentVersion,
35
+ assessment_fingerprint_version: assessmentVersion,
36
+ current_fingerprint_version: ASSESSMENT_SECURITY_SNAPSHOT_VERSION,
37
+ assessment_schema_epoch: safeText(result.assessment?.assessment_schema_epoch, 80),
38
+ current_schema_epoch: TOOL_SCHEMA_EPOCH,
39
+ assessment_created_at: safeTimestamp(result.assessment?.created_at),
40
+ assessment_expires_at: safeTimestamp(result.assessment?.expires_at),
41
+ config_changed: result.failure_reason === "assessment_stale_config",
42
+ changed_field_names: changedSections,
43
+ changed_config_sections: changedSections,
44
+ validator_module_path: VALIDATOR_MODULE_PATH,
45
+ validator_build_id: VALIDATOR_BUILD_ID,
46
+ watcher_instance_id: safeInstanceId(process.env.PATCHWARDEN_WATCHER_INSTANCE_ID),
47
+ observed_at: new Date().toISOString(),
48
+ };
49
+ }
50
+ /** Persist a bounded value-free failure summary without changing validation behavior. */
51
+ export function recordAssessmentValidationFailure(result) {
52
+ const diagnostic = buildAssessmentValidationFailureDiagnostic(result);
53
+ if (!diagnostic)
54
+ return false;
55
+ try {
56
+ const config = getConfig();
57
+ const assessmentsDir = getAssessmentsDir(config);
58
+ const file = guardPath(join(assessmentsDir, DIAGNOSTIC_FILE), config.workspaceRoot, config.assessmentsDir);
59
+ mkdirSync(dirname(file), { recursive: true });
60
+ atomicWriteJsonFileSync(file, diagnostic);
61
+ return true;
62
+ }
63
+ catch {
64
+ // Diagnostics must never replace the original fail-closed validation error.
65
+ return false;
66
+ }
67
+ }
68
+ export function readLastAssessmentValidationFailure() {
69
+ try {
70
+ const config = getConfig();
71
+ const file = guardPath(join(getAssessmentsDir(config), DIAGNOSTIC_FILE), config.workspaceRoot, config.assessmentsDir);
72
+ if (!existsSync(file))
73
+ return null;
74
+ const raw = readJsonObjectFileSync(file);
75
+ if (raw.diagnostic_version !== DIAGNOSTIC_VERSION)
76
+ return null;
77
+ const changedSections = Array.isArray(raw.changed_config_sections)
78
+ ? [...new Set(raw.changed_config_sections.map(safeCode).filter(Boolean))].sort().slice(0, 32)
79
+ : [];
80
+ return {
81
+ diagnostic_version: DIAGNOSTIC_VERSION,
82
+ error_code: safeCode(raw.error_code),
83
+ reason_code: safeCode(raw.reason_code),
84
+ assessment_id: safeAssessmentId(raw.assessment_id),
85
+ assessment_config_sha256: safeHash(raw.assessment_config_sha256),
86
+ current_config_sha256: safeHash(raw.current_config_sha256),
87
+ assessment_config_hash: safeHash(raw.assessment_config_hash),
88
+ current_config_hash: safeHash(raw.current_config_hash),
89
+ snapshot_version: safeText(raw.snapshot_version, 80),
90
+ assessment_fingerprint_version: safeText(raw.assessment_fingerprint_version, 80),
91
+ current_fingerprint_version: safeText(raw.current_fingerprint_version, 80) || ASSESSMENT_SECURITY_SNAPSHOT_VERSION,
92
+ assessment_schema_epoch: safeText(raw.assessment_schema_epoch, 80),
93
+ current_schema_epoch: safeText(raw.current_schema_epoch, 80) || TOOL_SCHEMA_EPOCH,
94
+ assessment_created_at: safeTimestamp(raw.assessment_created_at),
95
+ assessment_expires_at: safeTimestamp(raw.assessment_expires_at),
96
+ config_changed: raw.config_changed === true,
97
+ changed_field_names: Array.isArray(raw.changed_field_names)
98
+ ? [...new Set(raw.changed_field_names.map(safeCode).filter(Boolean))].sort().slice(0, 32)
99
+ : [],
100
+ changed_config_sections: changedSections,
101
+ validator_module_path: safeText(raw.validator_module_path, 120) || VALIDATOR_MODULE_PATH,
102
+ validator_build_id: safeText(raw.validator_build_id, 160) || VALIDATOR_BUILD_ID,
103
+ watcher_instance_id: safeInstanceId(raw.watcher_instance_id),
104
+ observed_at: safeTimestamp(raw.observed_at) || new Date(0).toISOString(),
105
+ };
106
+ }
107
+ catch {
108
+ return null;
109
+ }
110
+ }
111
+ function safeAssessmentId(value) {
112
+ return typeof value === "string" && /^assessment_\d{8}_\d{6}_[0-9a-f]{32}$/.test(value) ? value : null;
113
+ }
114
+ function safeInstanceId(value) {
115
+ return typeof value === "string" && /^[a-zA-Z0-9_-]{1,100}$/.test(value) ? value : null;
116
+ }
117
+ function safeCode(value) {
118
+ return typeof value === "string" && /^[a-z0-9_]{1,80}$/.test(value) ? value : "";
119
+ }
120
+ function safeHash(value) {
121
+ return typeof value === "string" && /^[0-9a-f]{64}$/.test(value) ? value : null;
122
+ }
123
+ function safeText(value, maxLength) {
124
+ return typeof value === "string" && value.length > 0
125
+ ? value.replace(/[\r\n]+/g, " ").slice(0, maxLength)
126
+ : null;
127
+ }
128
+ function safeTimestamp(value) {
129
+ return typeof value === "string" && Number.isFinite(Date.parse(value))
130
+ ? new Date(value).toISOString()
131
+ : null;
132
+ }
@@ -1,6 +1,8 @@
1
+ import { PatchWardenConfig } from "../config.js";
1
2
  import { type RepoSnapshot } from "../runner/changeCapture.js";
2
3
  import type { RiskAssessmentResult } from "../security/riskEngine.js";
3
4
  import type { TaskTemplateName, ChangePolicy } from "../tools/taskTemplates.js";
5
+ import { type SecuritySnapshotCategory } from "./securitySnapshot.js";
4
6
  export interface AgentAssessmentSummary {
5
7
  attempted: boolean;
6
8
  status: "completed" | "timed_out" | "non_zero_exit" | "parse_failed" | "read_only_violation" | "spawn_failed" | "not_run";
@@ -44,6 +46,12 @@ export interface AssessmentRecord {
44
46
  plan_id: string | null;
45
47
  policy_hash: string;
46
48
  tool_manifest_sha256: string;
49
+ execution_config_hash?: string;
50
+ execution_config_component_hashes?: Partial<Record<ExecutionConfigChangeCategory, string>>;
51
+ assessment_security_snapshot_version?: string;
52
+ assessment_security_snapshot_sha256?: string;
53
+ assessment_security_snapshot_component_hashes?: Partial<Record<ExecutionConfigChangeCategory, string>>;
54
+ assessment_schema_epoch?: string;
47
55
  workspace_fingerprint: string;
48
56
  workspace_snapshot_summary: {
49
57
  head: string | null;
@@ -62,10 +70,15 @@ export interface AssessmentRecord {
62
70
  agent: string;
63
71
  timeout_seconds?: number;
64
72
  change_policy?: ChangePolicy;
73
+ scope?: string[];
74
+ forbidden?: string[];
75
+ verification?: string[];
76
+ done_evidence?: string[];
65
77
  requires_confirm: boolean;
66
78
  confirmed: boolean;
67
79
  confirmed_at: string | null;
68
80
  confirm_code: string | null;
81
+ used_at?: string | null;
69
82
  agent_assessment_summary?: AgentAssessmentSummary | null;
70
83
  }
71
84
  export interface AssessmentCreateInput {
@@ -85,6 +98,10 @@ export interface AssessmentCreateInput {
85
98
  agent: string;
86
99
  timeout_seconds?: number;
87
100
  change_policy?: ChangePolicy;
101
+ scope?: string[];
102
+ forbidden?: string[];
103
+ verification?: string[];
104
+ done_evidence?: string[];
88
105
  snapshot: RepoSnapshot;
89
106
  assessment_id?: string;
90
107
  assessment_dir?: string;
@@ -94,7 +111,11 @@ export interface AssessmentValidationResult {
94
111
  valid: boolean;
95
112
  failure_reason: string | null;
96
113
  assessment: AssessmentRecord | null;
114
+ expected_hash?: string;
115
+ actual_hash?: string;
116
+ config_change_categories?: ExecutionConfigChangeCategory[];
97
117
  }
118
+ export type ExecutionConfigChangeCategory = SecuritySnapshotCategory | "allowed_test_commands" | "execution_config";
98
119
  export interface AssessmentConfirmationResult {
99
120
  assessment_id: string;
100
121
  decision: "needs_confirm";
@@ -111,6 +132,8 @@ export declare function generateAssessmentId(): string;
111
132
  export declare function createAssessmentDir(assessmentId: string): string;
112
133
  export declare function createAssessment(input: AssessmentCreateInput): AssessmentRecord;
113
134
  export declare function readAssessment(assessmentId: string): AssessmentRecord;
135
+ /** Atomically consume an Assessment before creating its one allowed task. */
136
+ export declare function markAssessmentUsed(assessmentId: string): AssessmentRecord;
114
137
  export declare function validateAssessmentFreshness(assessmentId: string, currentSnapshot: RepoSnapshot, options?: AssessmentValidationOptions): AssessmentValidationResult;
115
138
  /**
116
139
  * Records an explicit confirmation performed from the local PatchWarden CLI.
@@ -130,4 +153,20 @@ interface PolicyHashInput {
130
153
  schema_epoch: string;
131
154
  }
132
155
  export declare function computePolicyHash(input: PolicyHashInput): string;
156
+ interface ExecutionConfigHashInput {
157
+ agent: string;
158
+ tool_manifest_sha256: string;
159
+ tool_profile: string;
160
+ repo_path: string;
161
+ change_policy?: string | null;
162
+ template?: string | null;
163
+ verify_commands?: string[];
164
+ test_command?: string | null;
165
+ task_timeout_seconds?: number | null;
166
+ scope?: string[];
167
+ forbidden?: string[];
168
+ verification?: string[];
169
+ done_evidence?: string[];
170
+ }
171
+ export declare function computeExecutionConfigHash(config: PatchWardenConfig, input: ExecutionConfigHashInput): string;
133
172
  export {};