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
@@ -1,6 +1,7 @@
1
1
  import { resolve, join, dirname } from "node:path";
2
2
  import { fileURLToPath } from "node:url";
3
3
  import { PatchWardenError } from "../errors.js";
4
+ import { isPathChildOf, isSamePath } from "../utils/platform.js";
4
5
 
5
6
  const CRITICAL_RUNTIME_DIRS = ["dist", "src", "scripts", "release"];
6
7
 
@@ -10,19 +11,21 @@ const CRITICAL_RUNTIME_DIRS = ["dist", "src", "scripts", "release"];
10
11
  * and direct-session flows share the same protection.
11
12
  */
12
13
  export function guardRuntimeSelfModification(resolvedRepoPath: string): void {
13
- const runtimeRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
14
+ // runtimeGuard lives at <package-root>/{src,dist}/security/runtimeGuard.*.
15
+ const runtimeRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
16
+ const isRuntimeRoot = isSamePath(resolvedRepoPath, runtimeRoot);
14
17
  if (
15
- resolvedRepoPath === runtimeRoot ||
16
- resolvedRepoPath.startsWith(runtimeRoot + resolve("/")[0])
18
+ isRuntimeRoot ||
19
+ isPathChildOf(resolvedRepoPath, runtimeRoot)
17
20
  ) {
18
21
  const isCritical = CRITICAL_RUNTIME_DIRS.some((dir) => {
19
22
  const full = join(runtimeRoot, dir);
20
23
  return (
21
- resolvedRepoPath === full ||
22
- resolvedRepoPath.startsWith(full + resolve("/")[0])
24
+ isSamePath(resolvedRepoPath, full) ||
25
+ isPathChildOf(resolvedRepoPath, full)
23
26
  );
24
27
  });
25
- if (resolvedRepoPath === runtimeRoot || isCritical) {
28
+ if (isRuntimeRoot || isCritical) {
26
29
  throw new PatchWardenError(
27
30
  "runtime_self_modification_blocked",
28
31
  `repo_path points to the active PatchWarden runtime or its critical subdirectories.`,
@@ -0,0 +1,13 @@
1
+ import { timingSafeEqual } from "node:crypto";
2
+
3
+ /** Compare secret text without content-dependent byte comparison. */
4
+ export function timingSafeStringEqual(actual: string, expected: string): boolean {
5
+ const actualBytes = Buffer.from(actual, "utf-8");
6
+ const expectedBytes = Buffer.from(expected, "utf-8");
7
+ return actualBytes.length === expectedBytes.length
8
+ && timingSafeEqual(actualBytes, expectedBytes);
9
+ }
10
+
11
+ export function firstHeaderValue(value: string | string[] | undefined): string {
12
+ return Array.isArray(value) ? (value[0] || "") : (value || "");
13
+ }
@@ -8,22 +8,28 @@ const SENSITIVE_PATTERNS = [
8
8
  // Exact filenames
9
9
  /(?:^|[\\/])\.env$/i,
10
10
  /(?:^|[\\/])\.env\..+$/i,
11
+ /(?:^|[\\/])\.envrc$/i,
11
12
  /(?:^|[\\/])id_rsa$/i,
13
+ /(?:^|[\\/])id_dsa$/i,
12
14
  /(?:^|[\\/])id_ed25519$/i,
13
15
  /(?:^|[\\/])id_ecdsa$/i,
14
16
  /(?:^|[\\/])\.ssh[\\/]/i,
15
17
  // Credentials / tokens
16
- /(?:^|[\\/])credentials/i,
18
+ /(?:^|[\\/])credentials(?:\.(?:txt|json|ya?ml|toml|ini|cfg|db))?$/i,
17
19
  /(?:^|[\\/])\.?aws[\\/]credentials/i,
18
20
  /(?:^|[\\/])\.netrc$/i,
19
21
  /(?:^|[\\/])\.npmrc$/i,
20
22
  /(?:^|[\\/])\.pypirc$/i,
21
- /(?:^|[\\/])token/i,
23
+ /(?:^|[\\/])token$/i,
24
+ /(?:^|[\\/])tokens?\.(?:txt|json|ya?ml|toml|ini|cfg|db)$/i,
25
+ /(?:^|[\\/])token[-_](?:store|cache|data|secret|auth|credentials|backup|local|prod|production|dev|development)(?:\.[^\\/]+)?$/i,
26
+ /(?:^|[\\/])(?:access[_-]?token|refresh[_-]?token|api[_-]?key|client[_-]?secret)(?:\.(?:txt|json|ya?ml|ini|cfg))?$/i,
22
27
  // Private keys
23
28
  /(?:^|[\\/])[^.]+\.pem$/i,
24
29
  /(?:^|[\\/])[^.]+\.key$/i,
25
30
  /(?:^|[\\/])[^.]+\.pfx$/i,
26
31
  /(?:^|[\\/])[^.]+\.p12$/i,
32
+ /(?:^|[\\/])[^.]+\.ppk$/i,
27
33
  // Browser data
28
34
  /(?:^|[\\/])cookies/i,
29
35
  /(?:^|[\\/])web data$/i,
@@ -31,19 +37,32 @@ const SENSITIVE_PATTERNS = [
31
37
  /(?:^|[\\/])local state$/i,
32
38
  // Other sensitive files
33
39
  /(?:^|[\\/])\.git-credentials$/i,
40
+ /(?:^|[\\/])\.git[\\/]config$/i,
34
41
  /(?:^|[\\/])\.docker[\\/]config\.json$/i,
35
42
  /(?:^|[\\/])\.kube[\\/]config$/i,
43
+ /(?:^|[\\/])kubeconfig$/i,
44
+ /(?:^|[\\/])application_default_credentials\.json$/i,
45
+ /(?:^|[\\/])service[-_]?account(?:[-_][^\\/]+)?\.json$/i,
36
46
  /(?:^|[\\/])config\.json$/i, // generic config files often contain local tokens or service credentials
37
47
  ];
38
48
 
39
- // Files specifically inside .patchwarden are always allowed
40
- const SAFE_PREFIX = ".patchwarden";
49
+ /**
50
+ * Detect a Windows NTFS alternate data stream suffix. The colon in an
51
+ * absolute drive prefix (for example, C:\\) is the only permitted colon.
52
+ */
53
+ export function hasWindowsAlternateDataStream(filePath: string): boolean {
54
+ const withoutDrivePrefix = /^[A-Za-z]:[\\/]/.test(filePath)
55
+ ? filePath.slice(2)
56
+ : filePath;
57
+ return withoutDrivePrefix.includes(":");
58
+ }
41
59
 
42
60
  export function isSensitivePath(filePath: string): boolean {
43
- // Files inside .patchwarden are always safe
44
- const normalized = filePath.replace(/\\/g, "/");
45
- if (normalized.includes(`${SAFE_PREFIX}/`) || normalized.endsWith(SAFE_PREFIX)) {
46
- return false;
61
+ if (filePath.includes("\0")) return true;
62
+
63
+ // ADS can hide content behind an otherwise safe-looking filename.
64
+ if (hasWindowsAlternateDataStream(filePath)) {
65
+ return true;
47
66
  }
48
67
 
49
68
  for (const pattern of SENSITIVE_PATTERNS) {
@@ -17,8 +17,8 @@
17
17
 
18
18
  import { PatchWardenError } from "../errors.js";
19
19
  import { guardSensitivePath } from "./sensitiveGuard.js";
20
- import { TOOL_RISK_RANK, type PatchWardenToolMeta } from "../tools/toolRegistry.js";
21
- import type { ToolProfile } from "../tools/toolCatalog.js";
20
+ import { TOOL_RISK_RANK, type PatchWardenToolMeta } from "../tools/catalog/toolRegistry.js";
21
+ import type { ToolProfile } from "../tools/catalog/toolCatalog.js";
22
22
  import type { DiscoveryTokenRecord } from "./discoveryTokenStore.js";
23
23
 
24
24
  // ── 类型定义 ──────────────────────────────────────────────────────
@@ -0,0 +1,53 @@
1
+ import { existsSync, realpathSync, statSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { basename, normalize, resolve, win32 } from "node:path";
4
+
5
+ export interface WorkspaceRootValidation {
6
+ ok: boolean;
7
+ path: string;
8
+ reason: string | null;
9
+ category: "valid" | "missing" | "not_directory" | "unsafe_root";
10
+ }
11
+
12
+ function normalizeInput(value: string): string {
13
+ return /^[a-z]:[\\/]/i.test(value) ? win32.normalize(value) : normalize(resolve(value));
14
+ }
15
+
16
+ function comparable(value: string): string {
17
+ return normalizeInput(value).replace(/[\\/]+$/, "").toLowerCase();
18
+ }
19
+
20
+ export function unsafeWorkspaceRootLabel(value: string, userHome = homedir()): string | null {
21
+ const resolved = normalizeInput(value);
22
+ const trimmed = resolved.replace(/[\\/]+$/, "");
23
+ const windowsStyle = /^[a-z]:[\\/]/i.test(value) || /^\\\\/.test(value);
24
+ const leaf = (windowsStyle ? win32.basename(trimmed) : basename(trimmed)).toLowerCase();
25
+
26
+ if (/^[a-z]:$/i.test(trimmed) || trimmed === "" || trimmed === "/") return "drive root";
27
+ if (comparable(trimmed) === comparable(userHome)) return "user home directory";
28
+ if (leaf === "desktop") return "Desktop";
29
+ if (leaf === "downloads") return "Downloads";
30
+ if (leaf === "documents") return "Documents";
31
+ return null;
32
+ }
33
+
34
+ export function validateWorkspaceRoot(value: string, userHome = homedir()): WorkspaceRootValidation {
35
+ const lexicalPath = normalize(resolve(value || "."));
36
+ if (!existsSync(lexicalPath)) {
37
+ return { ok: false, path: lexicalPath, reason: "workspaceRoot does not exist", category: "missing" };
38
+ }
39
+ let resolved: string;
40
+ try {
41
+ resolved = normalize(realpathSync(lexicalPath));
42
+ if (!statSync(resolved).isDirectory()) {
43
+ return { ok: false, path: resolved, reason: "workspaceRoot is not a directory", category: "not_directory" };
44
+ }
45
+ } catch {
46
+ return { ok: false, path: lexicalPath, reason: "workspaceRoot is not accessible", category: "not_directory" };
47
+ }
48
+ const unsafeLabel = unsafeWorkspaceRootLabel(resolved, userHome);
49
+ if (unsafeLabel) {
50
+ return { ok: false, path: resolved, reason: `workspaceRoot cannot be ${unsafeLabel}`, category: "unsafe_root" };
51
+ }
52
+ return { ok: true, path: resolved, reason: null, category: "valid" };
53
+ }
package/src/smoke-test.ts CHANGED
@@ -27,22 +27,22 @@ import { tmpdir } from "node:os";
27
27
  import { fileURLToPath } from "node:url";
28
28
  import { spawnSync } from "node:child_process";
29
29
  import { loadConfig, getConfig, getTasksDir, reloadConfig } from "./config.js";
30
- import { savePlan } from "./tools/savePlan.js";
31
- import { getPlan } from "./tools/getPlan.js";
32
- import { createTask } from "./tools/createTask.js";
33
- import type { CreateTaskOutput, AssessOnlyOutput } from "./tools/createTask.js";
30
+ import { savePlan } from "./tools/goals/savePlan.js";
31
+ import { getPlan } from "./tools/goals/getPlan.js";
32
+ import { createTask } from "./tools/tasks/createTask.js";
33
+ import type { CreateTaskOutput, AssessOnlyOutput } from "./tools/tasks/createTask.js";
34
34
  import { confirmAssessment, readAssessment, computeWorkspaceFingerprint, createAssessment, type AssessmentRecord } from "./assessments/assessmentStore.js";
35
35
  import { captureRepoSnapshot } from "./runner/changeCapture.js";
36
- import { getTaskStatus } from "./tools/getTaskStatus.js";
37
- import { getResult, getDiff, getTestLog } from "./tools/taskOutputs.js";
38
- import { listWorkspace } from "./tools/listWorkspace.js";
39
- import { readWorkspaceFile } from "./tools/readWorkspaceFile.js";
40
- import { listTasks } from "./tools/listTasks.js";
41
- import { cancelTask } from "./tools/cancelTask.js";
42
- import { retryTask } from "./tools/retryTask.js";
43
- import { getTaskStdoutTail } from "./tools/getTaskStdoutTail.js";
44
- import { auditTask } from "./tools/auditTask.js";
45
- import { getTaskSummary } from "./tools/getTaskSummary.js";
36
+ import { getTaskStatus } from "./tools/tasks/getTaskStatus.js";
37
+ import { getResult, getDiff, getTestLog } from "./tools/tasks/taskOutputs.js";
38
+ import { listWorkspace } from "./tools/workspace/listWorkspace.js";
39
+ import { readWorkspaceFile } from "./tools/workspace/readWorkspaceFile.js";
40
+ import { listTasks } from "./tools/tasks/listTasks.js";
41
+ import { cancelTask } from "./tools/tasks/cancelTask.js";
42
+ import { retryTask } from "./tools/tasks/retryTask.js";
43
+ import { getTaskStdoutTail } from "./tools/tasks/getTaskStdoutTail.js";
44
+ import { auditTask } from "./tools/diagnostics/auditTask.js";
45
+ import { getTaskSummary } from "./tools/tasks/getTaskSummary.js";
46
46
  import { guardAgentCommand } from "./security/commandGuard.js";
47
47
  import { getToolDefs } from "./tools/registry.js";
48
48
  import {
@@ -50,15 +50,15 @@ import {
50
50
  CHATGPT_CORE_TOOL_NAMES,
51
51
  CHATGPT_DIRECT_TOOL_NAMES,
52
52
  selectToolsForProfile,
53
- } from "./tools/toolCatalog.js";
53
+ } from "./tools/catalog/toolCatalog.js";
54
54
  import { errorPayload } from "./errors.js";
55
55
  import { readWatcherStatus } from "./watcherStatus.js";
56
- import { createDirectSession } from "./tools/createDirectSession.js";
57
- import { searchWorkspace } from "./tools/searchWorkspace.js";
58
- import { applyPatch } from "./tools/applyPatch.js";
59
- import { runVerification } from "./tools/runVerification.js";
60
- import { finalizeDirectSession } from "./tools/finalizeDirectSession.js";
61
- import { auditSession } from "./tools/auditSession.js";
56
+ import { createDirectSession } from "./tools/direct/createDirectSession.js";
57
+ import { searchWorkspace } from "./tools/workspace/searchWorkspace.js";
58
+ import { applyPatch } from "./tools/workspace/applyPatch.js";
59
+ import { runVerification } from "./tools/tasks/runVerification.js";
60
+ import { finalizeDirectSession } from "./tools/direct/finalizeDirectSession.js";
61
+ import { auditSession } from "./tools/diagnostics/auditSession.js";
62
62
  import { readDirectSession, updateDirectSession } from "./direct/directSessionStore.js";
63
63
  import { createHash } from "node:crypto";
64
64
 
@@ -463,6 +463,8 @@ const sensitiveFiles = [
463
463
  "cookies.sqlite",
464
464
  ".git-credentials",
465
465
  "config.json",
466
+ ".patchwarden/.env",
467
+ ".patchwarden/credentials.json",
466
468
  ];
467
469
 
468
470
  for (const sf of sensitiveFiles) {
@@ -471,9 +473,8 @@ for (const sf of sensitiveFiles) {
471
473
  });
472
474
  }
473
475
 
474
- // Files inside .patchwarden should always be allowed
475
- await test("C. readWorkspaceFile allows .patchwarden/plans/...", () => {
476
- // This should work because .patchwarden files are whitelisted
476
+ // Ordinary PatchWarden artifacts remain readable through their dedicated tool.
477
+ await test("C. getPlan reads ordinary .patchwarden/plans artifacts", () => {
477
478
  const plan = savePlan({ title: "Allowlist Test", content: "test" });
478
479
  const result = getPlan({ plan_id: plan.plan_id });
479
480
  if (!result.content.includes("test")) throw new Error("Should allow .patchwarden reads");
@@ -1337,7 +1338,7 @@ await test("K8. assessment not found rejected", async () => {
1337
1338
  agent: "codex",
1338
1339
  repo_path: ".",
1339
1340
  execution_mode: "execute",
1340
- assessment_id: "assessment_20260101_" + "0".repeat(32),
1341
+ assessment_id: "assessment_20260101_000000_" + "0".repeat(32),
1341
1342
  });
1342
1343
  throw new Error("Should have rejected unknown assessment");
1343
1344
  } catch (e: any) {
@@ -1568,7 +1569,7 @@ await test("K16. assessment_short_id cannot execute", async () => {
1568
1569
  });
1569
1570
  throw new Error("Should have rejected short ID");
1570
1571
  } catch (e: any) {
1571
- if (!e.message?.includes("not found")) throw new Error(`Expected not found for short ID, got: ${e.message}`);
1572
+ if (e.reason !== "assessment_id_invalid") throw new Error(`Expected assessment_id_invalid for short ID, got: ${e.reason || e.message}`);
1572
1573
  }
1573
1574
  });
1574
1575
 
@@ -0,0 +1,4 @@
1
+ export * from "./toolCatalog.js";
2
+ export * from "./toolRegistry.js";
3
+ export * from "./toolSearch.js";
4
+ export * from "./toolUsageStats.js";
@@ -1,5 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
- import { PATCHWARDEN_VERSION, TOOL_SCHEMA_EPOCH } from "../version.js";
2
+ import { PATCHWARDEN_VERSION, TOOL_SCHEMA_EPOCH } from "../../version.js";
3
+ import { stableJsonStringify } from "../../utils/stableJson.js";
3
4
 
4
5
  export type ToolProfile = "full" | "chatgpt_core" | "chatgpt_direct" | "chatgpt_search";
5
6
 
@@ -124,7 +125,7 @@ export function buildToolCatalogSnapshot(tools: CatalogTool[], profile: ToolProf
124
125
  inputSchema: tool.inputSchema,
125
126
  }));
126
127
  const tool_manifest_sha256 = createHash("sha256")
127
- .update(stableJson(manifestInput))
128
+ .update(stableJsonStringify(manifestInput))
128
129
  .digest("hex");
129
130
  lastSnapshot = {
130
131
  server_version: PATCHWARDEN_VERSION,
@@ -140,13 +141,3 @@ export function buildToolCatalogSnapshot(tools: CatalogTool[], profile: ToolProf
140
141
  export function getLastToolCatalogSnapshot(): ToolCatalogSnapshot | null {
141
142
  return lastSnapshot ? { ...lastSnapshot, tool_names: [...lastSnapshot.tool_names] } : null;
142
143
  }
143
-
144
- function stableJson(value: unknown): string {
145
- if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
146
- if (value && typeof value === "object") {
147
- const entries = Object.entries(value as Record<string, unknown>)
148
- .sort(([left], [right]) => left.localeCompare(right));
149
- return `{${entries.map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`).join(",")}}`;
150
- }
151
- return JSON.stringify(value);
152
- }
@@ -7,8 +7,9 @@
7
7
  */
8
8
 
9
9
  import { createHash } from "node:crypto";
10
+ import { stableJsonStringify } from "../../utils/stableJson.js";
10
11
  import type { ToolProfile } from "./toolCatalog.js";
11
- import type { ToolDef } from "./registry.js";
12
+ import type { ToolDef } from "../definitions/toolDefs.js";
12
13
 
13
14
  // ── 风险分级 ──────────────────────────────────────────────────────
14
15
 
@@ -109,7 +110,7 @@ const STATIC_TOOL_META: Record<string, StaticToolMeta> = {
109
110
  summary: "列出已配置的本地 agent 及其可用性",
110
111
  profiles: ["full", "chatgpt_core"],
111
112
  modes: ["delegate", "diagnostic"],
112
- tags: ["agent", "list", "config", "codex", "opencode"],
113
+ tags: ["agent", "list", "config", "local-cli", "model"],
113
114
  aliases: ["agents", "list_agent"],
114
115
  risk: "readonly",
115
116
  requiresConfirmation: false,
@@ -254,7 +255,7 @@ const STATIC_TOOL_META: Record<string, StaticToolMeta> = {
254
255
  modes: ["delegate", "audit"],
255
256
  tags: ["safe", "audit", "checks", "acceptance", "task"],
256
257
  aliases: ["safe_audit", "audit_summary"],
257
- risk: "readonly",
258
+ risk: "workspace_write",
258
259
  requiresConfirmation: false,
259
260
  relatedTools: ["audit_task", "safe_result"],
260
261
  },
@@ -601,7 +602,7 @@ const STATIC_TOOL_META: Record<string, StaticToolMeta> = {
601
602
  modes: ["direct", "audit"],
602
603
  tags: ["safe", "direct", "finalize", "summary"],
603
604
  aliases: ["safe_finalize", "finalize_summary"],
604
- risk: "readonly",
605
+ risk: "workspace_write",
605
606
  requiresConfirmation: false,
606
607
  relatedTools: ["finalize_direct_session", "safe_direct_summary"],
607
608
  },
@@ -612,7 +613,7 @@ const STATIC_TOOL_META: Record<string, StaticToolMeta> = {
612
613
  modes: ["direct", "audit"],
613
614
  tags: ["safe", "direct", "audit", "session"],
614
615
  aliases: ["safe_direct_audit", "audit_direct_summary"],
615
- risk: "readonly",
616
+ risk: "workspace_write",
616
617
  requiresConfirmation: false,
617
618
  relatedTools: ["audit_session", "safe_finalize_direct_session"],
618
619
  },
@@ -857,17 +858,7 @@ const STATIC_TOOL_META: Record<string, StaticToolMeta> = {
857
858
  /**
858
859
  * 稳定 JSON 序列化:按字段名排序,避免 key 顺序导致 digest 漂移。
859
860
  */
860
- export function stableJsonStringify(value: unknown): string {
861
- if (Array.isArray(value)) {
862
- return `[${value.map(stableJsonStringify).join(",")}]`;
863
- }
864
- if (value !== null && typeof value === "object") {
865
- const entries = Object.entries(value as Record<string, unknown>)
866
- .sort(([left], [right]) => left.localeCompare(right));
867
- return `{${entries.map(([key, entry]) => `${JSON.stringify(key)}:${stableJsonStringify(entry)}`).join(",")}}`;
868
- }
869
- return JSON.stringify(value);
870
- }
861
+ export { stableJsonStringify };
871
862
 
872
863
  /**
873
864
  * 计算 inputSchema 的 sha256 digest。