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,165 +0,0 @@
1
- import { describe, it, beforeEach, afterEach } from "node:test";
2
- import { strict as assert } from "node:assert";
3
- import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from "node:fs";
4
- import { join } from "node:path";
5
- import { tmpdir } from "node:os";
6
- import { safeStatus } from "../../tools/safeStatus.js";
7
- function makeConfig(workspaceRoot) {
8
- return {
9
- workspaceRoot,
10
- plansDir: ".patchwarden/plans",
11
- tasksDir: ".patchwarden/tasks",
12
- assessmentsDir: ".patchwarden/assessments",
13
- assessmentTtlSeconds: 3600,
14
- agents: { codex: { command: "codex", args: ["exec", "{prompt}"] } },
15
- allowedTestCommands: ["npm test"],
16
- repoAllowedTestCommands: {},
17
- maxReadFileBytes: 200_000,
18
- defaultTaskTimeoutSeconds: 900,
19
- maxTaskTimeoutSeconds: 3600,
20
- watcherStaleSeconds: 30,
21
- directSessionsDir: ".patchwarden/direct-sessions",
22
- directSessionTtlSeconds: 3600,
23
- directMaxPatchBytes: 200_000,
24
- directMaxFileBytes: 500_000,
25
- };
26
- }
27
- describe("safeStatus", () => {
28
- let tempDir;
29
- let config;
30
- let tasksDir;
31
- beforeEach(() => {
32
- tempDir = mkdtempSync(join(tmpdir(), "pw-safestatus-"));
33
- config = makeConfig(tempDir);
34
- tasksDir = join(tempDir, ".patchwarden", "tasks");
35
- mkdirSync(tasksDir, { recursive: true });
36
- });
37
- afterEach(() => {
38
- rmSync(tempDir, { recursive: true, force: true });
39
- });
40
- function writeTaskStatus(taskId, status) {
41
- const taskDir = join(tasksDir, taskId);
42
- mkdirSync(taskDir, { recursive: true });
43
- writeFileSync(join(taskDir, "status.json"), JSON.stringify(status, null, 2), "utf-8");
44
- return taskDir;
45
- }
46
- function writeTaskRuntime(taskDir, runtime) {
47
- writeFileSync(join(taskDir, "runtime.json"), JSON.stringify(runtime, null, 2), "utf-8");
48
- }
49
- it("returns not_found for non-existent task", () => {
50
- const result = safeStatus("non-existent-task", config);
51
- assert.equal(result.task_id, "non-existent-task");
52
- assert.equal(result.status, "not_found");
53
- assert.equal(result.phase, null);
54
- assert.equal(result.error_code, null);
55
- assert.equal(result.error_summary, null);
56
- });
57
- it("returns correct status for pending task", () => {
58
- writeTaskStatus("task-pending-001", {
59
- task_id: "task-pending-001",
60
- status: "pending",
61
- phase: "queued",
62
- created_at: "2026-06-24T10:00:00Z",
63
- updated_at: "2026-06-24T10:00:01Z",
64
- });
65
- const result = safeStatus("task-pending-001", config);
66
- assert.equal(result.task_id, "task-pending-001");
67
- assert.equal(result.status, "pending");
68
- assert.equal(result.created_at, "2026-06-24T10:00:00Z");
69
- assert.equal(result.error_code, null);
70
- assert.equal(result.error_summary, null);
71
- });
72
- it("returns correct status for running task", () => {
73
- const taskDir = writeTaskStatus("task-running-001", {
74
- task_id: "task-running-001",
75
- status: "running",
76
- phase: "executing_agent",
77
- created_at: "2026-06-24T10:00:00Z",
78
- started_at: "2026-06-24T10:00:05Z",
79
- updated_at: "2026-06-24T10:00:10Z",
80
- });
81
- writeTaskRuntime(taskDir, {
82
- phase: "executing_agent",
83
- last_heartbeat_at: "2026-06-24T10:00:12Z",
84
- current_command: "codex exec",
85
- });
86
- const result = safeStatus("task-running-001", config);
87
- assert.equal(result.status, "running");
88
- assert.equal(result.phase, "executing_agent");
89
- assert.equal(result.current_command, "codex exec");
90
- assert.equal(result.last_heartbeat_at, "2026-06-24T10:00:12Z");
91
- assert.equal(result.started_at, "2026-06-24T10:00:05Z");
92
- });
93
- it("returns correct status for done task", () => {
94
- writeTaskStatus("task-done-001", {
95
- task_id: "task-done-001",
96
- status: "done",
97
- phase: "completed",
98
- created_at: "2026-06-24T10:00:00Z",
99
- started_at: "2026-06-24T10:00:05Z",
100
- finished_at: "2026-06-24T10:05:00Z",
101
- verify_status: "passed",
102
- });
103
- const result = safeStatus("task-done-001", config);
104
- assert.equal(result.status, "done");
105
- assert.equal(result.phase, "completed");
106
- assert.equal(result.finished_at, "2026-06-24T10:05:00Z");
107
- assert.equal(result.verify_status, "passed");
108
- assert.equal(result.error_code, null);
109
- });
110
- it("returns correct status for failed task", () => {
111
- writeTaskStatus("task-failed-001", {
112
- task_id: "task-failed-001",
113
- status: "failed_verification",
114
- phase: "failed_verification",
115
- created_at: "2026-06-24T10:00:00Z",
116
- started_at: "2026-06-24T10:00:05Z",
117
- finished_at: "2026-06-24T10:05:00Z",
118
- error: 'Verification command "npm test" exited with code 1.',
119
- verify_status: "failed",
120
- });
121
- const result = safeStatus("task-failed-001", config);
122
- assert.equal(result.status, "failed_verification");
123
- assert.equal(result.error_code, "failed_verification");
124
- assert.ok(result.error_summary);
125
- assert.ok(result.error_summary.includes("npm test"));
126
- });
127
- it("does not return diff or file content", () => {
128
- const taskDir = writeTaskStatus("task-safe-001", {
129
- task_id: "task-safe-001",
130
- status: "done",
131
- phase: "completed",
132
- created_at: "2026-06-24T10:00:00Z",
133
- });
134
- writeFileSync(join(taskDir, "result.md"), "# Secret result content", "utf-8");
135
- writeFileSync(join(taskDir, "git.diff"), "secret diff content", "utf-8");
136
- writeFileSync(join(taskDir, "test.log"), "secret test log", "utf-8");
137
- const result = safeStatus("task-safe-001", config);
138
- const resultStr = JSON.stringify(result);
139
- assert.ok(!resultStr.includes("Secret result content"));
140
- assert.ok(!resultStr.includes("secret diff content"));
141
- assert.ok(!resultStr.includes("secret test log"));
142
- });
143
- it("truncates long error messages", () => {
144
- const longError = "x".repeat(300);
145
- writeTaskStatus("task-longerror-001", {
146
- task_id: "task-longerror-001",
147
- status: "failed",
148
- phase: "failed",
149
- error: longError,
150
- });
151
- const result = safeStatus("task-longerror-001", config);
152
- assert.ok(result.error_summary.length < 300);
153
- assert.ok(result.error_summary.endsWith("..."));
154
- });
155
- it("returns artifact_status when present", () => {
156
- writeTaskStatus("task-artifact-001", {
157
- task_id: "task-artifact-001",
158
- status: "done",
159
- phase: "completed",
160
- artifact_status: "collected",
161
- });
162
- const result = safeStatus("task-artifact-001", config);
163
- assert.equal(result.artifact_status, "collected");
164
- });
165
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,171 +0,0 @@
1
- import { describe, it, beforeEach, afterEach } from "node:test";
2
- import { strict as assert } from "node:assert";
3
- import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from "node:fs";
4
- import { join } from "node:path";
5
- import { tmpdir } from "node:os";
6
- import { reloadConfig } from "../../config.js";
7
- import { safeDirectSummary, safeAuditDirectSession, safeDiffSummary, safeResult, safeTestSummary } from "../../tools/safeViews.js";
8
- let tempDir;
9
- let prevConfigEnv;
10
- function writeConfig() {
11
- const configPath = join(tempDir, "patchwarden.config.json");
12
- writeFileSync(configPath, JSON.stringify({
13
- workspaceRoot: tempDir,
14
- tasksDir: ".patchwarden/tasks",
15
- plansDir: ".patchwarden/plans",
16
- assessmentsDir: ".patchwarden/assessments",
17
- directSessionsDir: ".patchwarden/direct-sessions",
18
- agents: {},
19
- allowedTestCommands: ["npm test"],
20
- directAllowedCommands: ["npm test"],
21
- enableDirectProfile: true,
22
- }), "utf-8");
23
- prevConfigEnv = process.env.PATCHWARDEN_CONFIG;
24
- process.env.PATCHWARDEN_CONFIG = configPath;
25
- reloadConfig();
26
- }
27
- describe("safeViews", () => {
28
- beforeEach(() => {
29
- tempDir = mkdtempSync(join(tmpdir(), "pw-safeviews-"));
30
- writeConfig();
31
- });
32
- afterEach(() => {
33
- if (prevConfigEnv === undefined)
34
- delete process.env.PATCHWARDEN_CONFIG;
35
- else
36
- process.env.PATCHWARDEN_CONFIG = prevConfigEnv;
37
- reloadConfig();
38
- rmSync(tempDir, { recursive: true, force: true });
39
- });
40
- it("returns bounded task summaries without log or diff content", () => {
41
- const taskId = "task-safe-001";
42
- const taskDir = join(tempDir, ".patchwarden", "tasks", taskId);
43
- mkdirSync(taskDir, { recursive: true });
44
- writeFileSync(join(taskDir, "status.json"), JSON.stringify({
45
- task_id: taskId,
46
- status: "done_by_agent",
47
- phase: "done_by_agent",
48
- repo_path: "repo",
49
- resolved_repo_path: join(tempDir, "repo"),
50
- verify_status: "passed",
51
- }), "utf-8");
52
- writeFileSync(join(taskDir, "result.json"), JSON.stringify({
53
- task_id: taskId,
54
- status: "done_by_agent",
55
- summary: "Finished safely",
56
- changed_files: [{ path: "src/index.ts", change: "modified" }],
57
- verify_status: "passed",
58
- warnings: [],
59
- }), "utf-8");
60
- writeFileSync(join(taskDir, "verify.json"), JSON.stringify({
61
- status: "passed",
62
- commands: [{ command: "npm test", status: "passed", exit_code: 0, stdout_tail: "SECRET_STDOUT", stderr_tail: "SECRET_STDERR" }],
63
- }), "utf-8");
64
- writeFileSync(join(taskDir, "changed-files.json"), JSON.stringify({
65
- changed_files: [{ path: "src/index.ts", change: "modified", tracked: true, ignored: false, kind: "source" }],
66
- additions: 1,
67
- deletions: 0,
68
- diff_available: true,
69
- diff_truncated: false,
70
- patch_mode: "textual",
71
- artifact_hygiene: { counts: { source_changes: 1 }, source_changes: [], tracked_build_artifacts: [], ignored_untracked_artifacts: [], runtime_generated_files: [], suspicious_changes: [] },
72
- }), "utf-8");
73
- writeFileSync(join(taskDir, "git.diff"), "SECRET_DIFF", "utf-8");
74
- writeFileSync(join(taskDir, "test.log"), "SECRET_TEST_LOG", "utf-8");
75
- const payload = JSON.stringify({
76
- result: safeResult(taskId),
77
- tests: safeTestSummary(taskId),
78
- diff: safeDiffSummary(taskId),
79
- });
80
- assert.ok(!payload.includes("SECRET_STDOUT"));
81
- assert.ok(!payload.includes("SECRET_STDERR"));
82
- assert.ok(!payload.includes("SECRET_DIFF"));
83
- assert.ok(!payload.includes("SECRET_TEST_LOG"));
84
- assert.ok(payload.includes("src/index.ts"));
85
- });
86
- it("treats corrupted safe-view JSON artifacts as empty summaries", () => {
87
- const taskId = "task-safe-corrupt";
88
- const taskDir = join(tempDir, ".patchwarden", "tasks", taskId);
89
- mkdirSync(taskDir, { recursive: true });
90
- writeFileSync(join(taskDir, "status.json"), JSON.stringify({
91
- task_id: taskId,
92
- status: "done_by_agent",
93
- phase: "done_by_agent",
94
- repo_path: "repo",
95
- resolved_repo_path: join(tempDir, "repo"),
96
- verify_status: "passed",
97
- }), "utf-8");
98
- writeFileSync(join(taskDir, "result.json"), JSON.stringify({
99
- task_id: taskId,
100
- status: "done_by_agent",
101
- summary: "Finished safely",
102
- changed_files: [],
103
- verify_status: "passed",
104
- warnings: [],
105
- }), "utf-8");
106
- writeFileSync(join(taskDir, "verify.json"), "\uFEFF{not json", "utf-8");
107
- writeFileSync(join(taskDir, "changed-files.json"), "{not json", "utf-8");
108
- const tests = safeTestSummary(taskId);
109
- const diff = safeDiffSummary(taskId);
110
- assert.equal(tests.command_count, 0);
111
- assert.equal(diff.changed_files_total, 0);
112
- });
113
- it("returns Direct safe summaries without verification tails", () => {
114
- const sessionId = "direct-safe-001";
115
- const sessionDir = join(tempDir, ".patchwarden", "direct-sessions", sessionId);
116
- mkdirSync(sessionDir, { recursive: true });
117
- const session = {
118
- session_id: sessionId,
119
- title: "safe direct",
120
- repo_path: "repo",
121
- resolved_repo_path: join(tempDir, "repo"),
122
- created_at: new Date().toISOString(),
123
- expires_at: new Date(Date.now() + 60_000).toISOString(),
124
- server_version: "1.0.0",
125
- schema_epoch: "test",
126
- tool_manifest_sha256: "x".repeat(64),
127
- workspace_snapshot_before: { head: null, status: "", files: {}, workspace_dirty: false, warnings: [], is_git: false },
128
- workspace_fingerprint_before: "fingerprint",
129
- allowed_commands: ["npm test"],
130
- operations: [],
131
- verification_runs: [{
132
- command: "npm test",
133
- exit_code: 0,
134
- passed: true,
135
- timed_out: false,
136
- stdout_tail: "SECRET_DIRECT_STDOUT",
137
- stderr_tail: "SECRET_DIRECT_STDERR",
138
- started_at: new Date().toISOString(),
139
- finished_at: new Date().toISOString(),
140
- log_path: join(sessionDir, "verification.log"),
141
- }],
142
- finalized: true,
143
- finalized_at: new Date().toISOString(),
144
- audited: false,
145
- change_artifacts: {
146
- changed_files: [],
147
- diff: "SECRET_DIRECT_DIFF",
148
- diff_available: true,
149
- diff_truncated: false,
150
- diff_size_bytes: 18,
151
- additions: 0,
152
- deletions: 0,
153
- file_stats: [],
154
- workspace_dirty_before: false,
155
- workspace_dirty_after: false,
156
- patch_mode: "no_changes",
157
- unavailable_reason: null,
158
- artifact_hygiene: { counts: { source_changes: 0, tracked_build_artifacts: 0, ignored_untracked_artifacts: 0, runtime_generated_files: 0, suspicious_changes: 0 }, source_changes: [], tracked_build_artifacts: [], ignored_untracked_artifacts: [], runtime_generated_files: [], suspicious_changes: [] },
159
- },
160
- };
161
- writeFileSync(join(sessionDir, "session.json"), JSON.stringify(session, null, 2), "utf-8");
162
- const payload = JSON.stringify({
163
- summary: safeDirectSummary(sessionId),
164
- audit: safeAuditDirectSession(sessionId),
165
- });
166
- assert.ok(!payload.includes("SECRET_DIRECT_STDOUT"));
167
- assert.ok(!payload.includes("SECRET_DIRECT_STDERR"));
168
- assert.ok(!payload.includes("SECRET_DIRECT_DIFF"));
169
- assert.ok(payload.includes("npm test"));
170
- });
171
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,176 +0,0 @@
1
- import { describe, it } from "node:test";
2
- import { strict as assert } from "node:assert";
3
- import { computeSchemaDigest } from "../../tools/toolRegistry.js";
4
- import { CHATGPT_CORE_TOOL_NAMES } from "../../tools/toolCatalog.js";
5
- import { checkRegistrySchemaDigest, checkChatgptCoreManifestStable, checkNewToolsProfileAppend, runAllSchemaDriftChecks, } from "../../tools/schemaDriftCheck.js";
6
- // ── 测试夹具构造工具 ─────────────────────────────────────────────
7
- const SAMPLE_SCHEMA = {
8
- type: "object",
9
- properties: {
10
- task_id: { type: "string", description: "Task ID" },
11
- },
12
- required: ["task_id"],
13
- };
14
- const OTHER_SCHEMA = {
15
- type: "object",
16
- properties: {
17
- plan_id: { type: "string", description: "Plan ID" },
18
- },
19
- required: ["plan_id"],
20
- };
21
- const CORE_COUNT = CHATGPT_CORE_TOOL_NAMES.length;
22
- /** 构造一个满足 PatchWardenToolMeta 接口的元数据对象。 */
23
- function makeMeta(opts) {
24
- const schema = opts.inputSchema ?? SAMPLE_SCHEMA;
25
- return {
26
- name: opts.name,
27
- title: `Tool ${opts.name}`,
28
- summary: `summary for ${opts.name}`,
29
- description: `description for ${opts.name}`,
30
- profiles: opts.profiles ?? ["full"],
31
- modes: ["diagnostic"],
32
- tags: [],
33
- aliases: [],
34
- risk: "readonly",
35
- requiresConfirmation: false,
36
- inputSchemaDigest: opts.inputSchemaDigest ?? computeSchemaDigest(schema),
37
- };
38
- }
39
- /** 构造 toolDefs Map。 */
40
- function makeToolDefs(entries) {
41
- const map = new Map();
42
- for (const e of entries) {
43
- map.set(e.name, {
44
- inputSchema: e.inputSchema ?? SAMPLE_SCHEMA,
45
- });
46
- }
47
- return map;
48
- }
49
- /** 构造包含全部 21 个 chatgpt_core 工具(顺序一致)的 toolDefs Map。 */
50
- function makeCoreToolDefs() {
51
- return makeToolDefs([...CHATGPT_CORE_TOOL_NAMES].map((name) => ({ name })));
52
- }
53
- /** 构造一个 registry:所有工具都属于 chatgpt_core profile。 */
54
- function makeCoreRegistry(count) {
55
- const names = [...CHATGPT_CORE_TOOL_NAMES].slice(0, count);
56
- return names.map((name) => makeMeta({ name, profiles: ["chatgpt_core"] }));
57
- }
58
- // ── 1. checkRegistrySchemaDigest ─────────────────────────────────
59
- describe("checkRegistrySchemaDigest", () => {
60
- it("digest 一致时 ok=true,warnings 为空", () => {
61
- const schema = { type: "object", properties: { a: { type: "string" } } };
62
- const registry = [makeMeta({ name: "tool_a", inputSchema: schema })];
63
- const toolDefs = makeToolDefs([{ name: "tool_a", inputSchema: schema }]);
64
- const result = checkRegistrySchemaDigest(registry, toolDefs);
65
- assert.equal(result.ok, true);
66
- assert.deepEqual(result.warnings, []);
67
- });
68
- it("digest 不一致时 ok=false,warnings 包含工具名和两个 digest", () => {
69
- const registrySchema = SAMPLE_SCHEMA;
70
- const actualSchema = OTHER_SCHEMA;
71
- // registry 记录的是 registrySchema 的 digest,但实际 toolDef 用的是 actualSchema
72
- const registry = [
73
- makeMeta({ name: "tool_drift", inputSchema: registrySchema }),
74
- ];
75
- const toolDefs = makeToolDefs([{ name: "tool_drift", inputSchema: actualSchema }]);
76
- const result = checkRegistrySchemaDigest(registry, toolDefs);
77
- assert.equal(result.ok, false);
78
- assert.equal(result.warnings.length, 1);
79
- const warning = result.warnings[0];
80
- const expectedRegistryDigest = computeSchemaDigest(registrySchema);
81
- const expectedActualDigest = computeSchemaDigest(actualSchema);
82
- assert.ok(warning.includes("tool_drift"), "warning 应包含工具名");
83
- assert.ok(warning.includes(expectedRegistryDigest), "warning 应包含 registry digest");
84
- assert.ok(warning.includes(expectedActualDigest), "warning 应包含 actual digest");
85
- });
86
- it("toolDefs 中找不到工具时跳过(不报 drift)", () => {
87
- const registry = [makeMeta({ name: "missing_tool" })];
88
- const toolDefs = makeToolDefs([]); // 空映射
89
- const result = checkRegistrySchemaDigest(registry, toolDefs);
90
- assert.equal(result.ok, true);
91
- assert.deepEqual(result.warnings, []);
92
- });
93
- });
94
- // ── 2. checkChatgptCoreManifestStable ────────────────────────────
95
- describe("checkChatgptCoreManifestStable", () => {
96
- it("21 工具全部存在时 ok=true", () => {
97
- const toolDefs = makeCoreToolDefs();
98
- const result = checkChatgptCoreManifestStable(toolDefs);
99
- assert.equal(result.ok, true);
100
- assert.deepEqual(result.warnings, []);
101
- });
102
- it("缺少工具时 ok=false", () => {
103
- // 移除最后一个工具,使 chatgpt_core 工具数为 20
104
- const names = [...CHATGPT_CORE_TOOL_NAMES].slice(0, CORE_COUNT - 1);
105
- const toolDefs = makeToolDefs(names.map((name) => ({ name })));
106
- const result = checkChatgptCoreManifestStable(toolDefs);
107
- assert.equal(result.ok, false);
108
- assert.equal(result.warnings.length, 1);
109
- assert.ok(result.warnings[0].includes("missing tools"), "warning 应包含 missing tools");
110
- assert.ok(result.warnings[0].includes(CHATGPT_CORE_TOOL_NAMES[CORE_COUNT - 1]), "warning 应包含缺失的工具名");
111
- });
112
- });
113
- // ── 3. checkNewToolsProfileAppend ────────────────────────────────
114
- describe("checkNewToolsProfileAppend", () => {
115
- it("chatgpt_core 21 工具时 ok=true", () => {
116
- const registry = makeCoreRegistry(CORE_COUNT);
117
- const result = checkNewToolsProfileAppend(registry);
118
- assert.equal(result.ok, true);
119
- assert.deepEqual(result.warnings, []);
120
- });
121
- it("chatgpt_core 工具数不为 21 时 ok=false", () => {
122
- // 22 个 chatgpt_core 工具:21 个标准工具 + 1 个重复名(模拟新工具追加)
123
- const registry = makeCoreRegistry(CORE_COUNT);
124
- registry.push(makeMeta({ name: "extra_tool", profiles: ["chatgpt_core"] }));
125
- const result = checkNewToolsProfileAppend(registry);
126
- assert.equal(result.ok, false);
127
- assert.equal(result.warnings.length, 1);
128
- assert.ok(result.warnings[0].includes(`expected ${CORE_COUNT}`), "warning 应包含预期数量 21");
129
- assert.ok(result.warnings[0].includes(`got ${CORE_COUNT + 1}`), "warning 应包含实际数量 22");
130
- });
131
- });
132
- // ── 4. runAllSchemaDriftChecks ───────────────────────────────────
133
- describe("runAllSchemaDriftChecks", () => {
134
- it("无 drift 时 ok=true", () => {
135
- // registry 与 toolDefs 完全一致,且 chatgpt_core 工具数为 21
136
- const registry = makeCoreRegistry(CORE_COUNT);
137
- const toolDefs = makeCoreToolDefs();
138
- const result = runAllSchemaDriftChecks(registry, toolDefs);
139
- assert.equal(result.ok, true);
140
- assert.deepEqual(result.warnings, []);
141
- });
142
- it("有 drift 时 ok=false,warnings 合并正确", () => {
143
- // 构造同时触发三个检查的 drift:
144
- // - check 1: 一个工具 digest 不一致
145
- // - check 2: toolDefs 缺少一个 chatgpt_core 工具(数量 16)
146
- // - check 3: registry 有 22 个 chatgpt_core 工具
147
- const driftedSchema = OTHER_SCHEMA;
148
- const registry = makeCoreRegistry(CORE_COUNT);
149
- // 第一个工具的 digest 指向 SAMPLE_SCHEMA,但 toolDefs 中该工具用 driftedSchema
150
- registry[0] = makeMeta({
151
- name: CHATGPT_CORE_TOOL_NAMES[0],
152
- inputSchema: SAMPLE_SCHEMA,
153
- profiles: ["chatgpt_core"],
154
- });
155
- // 追加一个额外的 chatgpt_core 工具,使 profile 工具数变为 22
156
- registry.push(makeMeta({ name: "extra_tool", profiles: ["chatgpt_core"] }));
157
- // toolDefs:第一个工具用 driftedSchema(触发 check 1),且移除最后一个核心工具(触发 check 2)
158
- const coreNames = [...CHATGPT_CORE_TOOL_NAMES];
159
- const toolDefEntries = coreNames.slice(0, CORE_COUNT - 1).map((name) => ({
160
- name,
161
- inputSchema: name === CHATGPT_CORE_TOOL_NAMES[0] ? driftedSchema : SAMPLE_SCHEMA,
162
- }));
163
- const toolDefs = makeToolDefs(toolDefEntries);
164
- const result = runAllSchemaDriftChecks(registry, toolDefs);
165
- assert.equal(result.ok, false);
166
- // 至少包含来自三个检查的警告
167
- assert.ok(result.warnings.length >= 3, `应有至少 3 条警告,实际 ${result.warnings.length}`);
168
- // 验证三类警告都存在
169
- const hasDigestDrift = result.warnings.some((w) => w.startsWith("Schema drift for tool"));
170
- const hasManifestMissing = result.warnings.some((w) => w.includes("missing tools"));
171
- const hasProfileCount = result.warnings.some((w) => w.includes("profile tool count changed"));
172
- assert.ok(hasDigestDrift, "应包含 registry digest drift 警告");
173
- assert.ok(hasManifestMissing, "应包含 manifest 缺失工具警告");
174
- assert.ok(hasProfileCount, "应包含 profile 数量变化警告");
175
- });
176
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,104 +0,0 @@
1
- import { describe, it } from "node:test";
2
- import { strict as assert } from "node:assert";
3
- import { isSensitivePath, guardSensitivePath } from "../../security/sensitiveGuard.js";
4
- import { PatchWardenError } from "../../errors.js";
5
- describe("isSensitivePath", () => {
6
- it("blocks .env files", () => {
7
- assert.equal(isSensitivePath(".env"), true);
8
- assert.equal(isSensitivePath("path/to/.env"), true);
9
- assert.equal(isSensitivePath(".env.production"), true);
10
- assert.equal(isSensitivePath(".env.local"), true);
11
- });
12
- it("blocks .ENV case insensitive", () => {
13
- assert.equal(isSensitivePath(".ENV"), true);
14
- assert.equal(isSensitivePath("path/to/.ENV"), true);
15
- assert.equal(isSensitivePath(".Env.Production"), true);
16
- });
17
- it("blocks config.json case insensitive", () => {
18
- assert.equal(isSensitivePath("config.json"), true);
19
- assert.equal(isSensitivePath("Config.json"), true);
20
- assert.equal(isSensitivePath("CONFIG.JSON"), true);
21
- assert.equal(isSensitivePath("path/to/Config.json"), true);
22
- });
23
- it("blocks SSH keys", () => {
24
- assert.equal(isSensitivePath("id_rsa"), true);
25
- assert.equal(isSensitivePath("id_ed25519"), true);
26
- assert.equal(isSensitivePath("id_ecdsa"), true);
27
- assert.equal(isSensitivePath("path/to/id_rsa"), true);
28
- assert.equal(isSensitivePath(".ssh/id_rsa"), true);
29
- });
30
- it("blocks credentials and tokens", () => {
31
- assert.equal(isSensitivePath("credentials"), true);
32
- assert.equal(isSensitivePath("path/to/credentials.json"), true);
33
- assert.equal(isSensitivePath("token.txt"), true);
34
- // access_token does NOT match because the pattern requires token at start or after /
35
- assert.equal(isSensitivePath("access_token"), false);
36
- assert.equal(isSensitivePath("path/to/token"), true);
37
- assert.equal(isSensitivePath(".netrc"), true);
38
- assert.equal(isSensitivePath(".npmrc"), true);
39
- assert.equal(isSensitivePath(".pypirc"), true);
40
- });
41
- it("blocks private key files", () => {
42
- assert.equal(isSensitivePath("server.pem"), true);
43
- assert.equal(isSensitivePath("private.key"), true);
44
- assert.equal(isSensitivePath("cert.pfx"), true);
45
- assert.equal(isSensitivePath("cert.p12"), true);
46
- });
47
- it("blocks browser data files", () => {
48
- assert.equal(isSensitivePath("cookies"), true);
49
- assert.equal(isSensitivePath("cookies.db"), true);
50
- assert.equal(isSensitivePath("Web Data"), true);
51
- assert.equal(isSensitivePath("Login Data"), true);
52
- assert.equal(isSensitivePath("Local State"), true);
53
- });
54
- it("blocks .git-credentials", () => {
55
- assert.equal(isSensitivePath(".git-credentials"), true);
56
- assert.equal(isSensitivePath("path/to/.git-credentials"), true);
57
- });
58
- it("blocks docker and kube config", () => {
59
- assert.equal(isSensitivePath(".docker/config.json"), true);
60
- assert.equal(isSensitivePath(".kube/config"), true);
61
- });
62
- it("allows .patchwarden safe prefix", () => {
63
- assert.equal(isSensitivePath(".patchwarden/tasks/task-001/status.json"), false);
64
- assert.equal(isSensitivePath(".patchwarden/config.json"), false);
65
- assert.equal(isSensitivePath(".patchwarden"), false);
66
- });
67
- it("allows non-sensitive files", () => {
68
- assert.equal(isSensitivePath("src/main.ts"), false);
69
- assert.equal(isSensitivePath("README.md"), false);
70
- assert.equal(isSensitivePath("package.json"), false);
71
- assert.equal(isSensitivePath("docs/guide.md"), false);
72
- });
73
- it("handles Windows backslash paths", () => {
74
- assert.equal(isSensitivePath("path\\to\\.env"), true);
75
- assert.equal(isSensitivePath("path\\to\\config.json"), true);
76
- assert.equal(isSensitivePath(".patchwarden\\tasks\\status.json"), false);
77
- });
78
- it("handles null byte in path", () => {
79
- // config.json with null byte appended — the $ anchor means this won't match
80
- // because the string doesn't end with "config.json"
81
- const nullPath = "config.json\x00.txt";
82
- assert.equal(isSensitivePath(nullPath), false);
83
- // But plain config.json still matches
84
- assert.equal(isSensitivePath("config.json"), true);
85
- });
86
- it("handles Unicode lookalike characters", () => {
87
- // Full-width dot (U+FF0E) should not match .env pattern
88
- // This is expected behavior — Unicode lookalikes are NOT matched
89
- const fullWidthPath = "\uFF0Eenv";
90
- assert.equal(isSensitivePath(fullWidthPath), false);
91
- });
92
- });
93
- describe("guardSensitivePath", () => {
94
- it("throws PatchWardenError for sensitive paths", () => {
95
- assert.throws(() => guardSensitivePath(".env"), (err) => err instanceof PatchWardenError && err.reason === "sensitive_path_blocked");
96
- });
97
- it("does not throw for non-sensitive paths", () => {
98
- assert.doesNotThrow(() => guardSensitivePath("src/main.ts"));
99
- assert.doesNotThrow(() => guardSensitivePath("README.md"));
100
- });
101
- it("does not throw for .patchwarden paths", () => {
102
- assert.doesNotThrow(() => guardSensitivePath(".patchwarden/tasks/status.json"));
103
- });
104
- });
@@ -1 +0,0 @@
1
- export {};