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,287 +0,0 @@
1
- import { describe, it } from "node:test";
2
- import { strict as assert } from "node:assert";
3
- import { routeAgent, AGENT_ROUTING_RULES, } from "../../agents/agentRouter.js";
4
- import { PatchWardenError } from "../../errors.js";
5
- // ── Helpers ───────────────────────────────────────────────────────
6
- const ALL_AGENTS = ["codex", "opencode", "claude", "patchwarden-direct", "patchwarden-audit"];
7
- function scopeFiles(count) {
8
- return Array.from({ length: count }, (_, i) => `file_${i}.ts`);
9
- }
10
- // ── Tests ─────────────────────────────────────────────────────────
11
- describe("routeAgent", () => {
12
- // ── 1. 大规模代码修改 → opencode ────────────────────────────────
13
- it("scope 15 个文件 → opencode, fallback=false, reason 含 'large scope'", () => {
14
- const result = routeAgent({
15
- goal: "大规模修改",
16
- scope: scopeFiles(15),
17
- configuredAgents: ALL_AGENTS,
18
- });
19
- assert.equal(result.recommended_agent, "opencode");
20
- assert.equal(result.fallback, false);
21
- assert.ok(result.reason.includes("large scope"), `reason should contain 'large scope', got: ${result.reason}`);
22
- assert.ok(result.reason.includes("15"), `reason should contain file count '15', got: ${result.reason}`);
23
- assert.ok(ALL_AGENTS.includes(result.recommended_agent));
24
- });
25
- // ── 2. 单文件修改 → patchwarden-direct ──────────────────────────
26
- it("scope 1 个文件 → patchwarden-direct, fallback=false", () => {
27
- const result = routeAgent({
28
- goal: "修复一个 bug",
29
- scope: ["src/index.ts"],
30
- configuredAgents: ALL_AGENTS,
31
- });
32
- assert.equal(result.recommended_agent, "patchwarden-direct");
33
- assert.equal(result.fallback, false);
34
- assert.ok(ALL_AGENTS.includes(result.recommended_agent));
35
- });
36
- // ── 3. 文档整理 → claude ────────────────────────────────────────
37
- it("goal 含 '更新 README 文档' → claude(若 configured)", () => {
38
- const result = routeAgent({
39
- goal: "更新 README 文档",
40
- configuredAgents: ALL_AGENTS,
41
- });
42
- assert.equal(result.recommended_agent, "claude");
43
- assert.equal(result.fallback, false);
44
- assert.ok(result.reason.includes("documentation"), `reason should contain 'documentation', got: ${result.reason}`);
45
- assert.ok(ALL_AGENTS.includes(result.recommended_agent));
46
- });
47
- it("goal 含文档关键词但 claude 未配置 → fallback 到 opencode", () => {
48
- const configured = ["codex", "opencode"];
49
- const result = routeAgent({
50
- goal: "更新 README 文档",
51
- configuredAgents: configured,
52
- });
53
- assert.equal(result.recommended_agent, "opencode");
54
- assert.equal(result.fallback, true);
55
- assert.ok(configured.includes(result.recommended_agent));
56
- });
57
- it("goal 含文档关键词但 claude 和 opencode 都未配置 → fallback 到第一个", () => {
58
- const configured = ["codex"];
59
- const result = routeAgent({
60
- goal: "更新 README 文档",
61
- configuredAgents: configured,
62
- });
63
- assert.equal(result.recommended_agent, "codex");
64
- assert.equal(result.fallback, true);
65
- assert.ok(configured.includes(result.recommended_agent));
66
- });
67
- // ── 4. 复杂推理和重构 → codex ───────────────────────────────────
68
- it("goal 含 '跨模块重构' → codex", () => {
69
- const result = routeAgent({
70
- goal: "跨模块重构认证模块",
71
- configuredAgents: ALL_AGENTS,
72
- });
73
- assert.equal(result.recommended_agent, "codex");
74
- assert.equal(result.fallback, false);
75
- assert.ok(result.reason.includes("refactor"), `reason should contain 'refactor', got: ${result.reason}`);
76
- assert.ok(ALL_AGENTS.includes(result.recommended_agent));
77
- });
78
- it("goal 含 'redesign' → codex", () => {
79
- const result = routeAgent({
80
- goal: "Redesign the data layer",
81
- configuredAgents: ALL_AGENTS,
82
- });
83
- assert.equal(result.recommended_agent, "codex");
84
- assert.equal(result.fallback, false);
85
- assert.ok(ALL_AGENTS.includes(result.recommended_agent));
86
- });
87
- // ── 5. 验收审计 → patchwarden-audit ─────────────────────────────
88
- it("goal 含 '审计任务' → patchwarden-audit", () => {
89
- const result = routeAgent({
90
- goal: "执行验收审计任务",
91
- configuredAgents: ALL_AGENTS,
92
- });
93
- assert.equal(result.recommended_agent, "patchwarden-audit");
94
- assert.equal(result.fallback, false);
95
- assert.ok(result.reason.includes("audit"), `reason should contain 'audit', got: ${result.reason}`);
96
- assert.ok(ALL_AGENTS.includes(result.recommended_agent));
97
- });
98
- it("goal 含 'verify' → patchwarden-audit", () => {
99
- const result = routeAgent({
100
- goal: "verify the release artifacts",
101
- configuredAgents: ALL_AGENTS,
102
- });
103
- assert.equal(result.recommended_agent, "patchwarden-audit");
104
- assert.equal(result.fallback, false);
105
- assert.ok(ALL_AGENTS.includes(result.recommended_agent));
106
- });
107
- // ── 6. 无匹配 → fallback 到 configuredAgents[0] ────────────────
108
- it("无匹配(goal='普通任务', scope=['a.ts','b.ts'])→ fallback=true, recommended_agent=configuredAgents[0]", () => {
109
- const configured = ["codex", "opencode"];
110
- const result = routeAgent({
111
- goal: "普通任务",
112
- scope: ["a.ts", "b.ts"],
113
- configuredAgents: configured,
114
- });
115
- assert.equal(result.recommended_agent, configured[0]);
116
- assert.equal(result.fallback, true);
117
- assert.ok(configured.includes(result.recommended_agent));
118
- });
119
- // ── 7. 白名单约束:推荐结果必须在 configuredAgents 中 ──────────
120
- it("所有场景断言 result.recommended_agent 在 configuredAgents 中", () => {
121
- const configured = ["codex", "opencode"];
122
- const inputs = [
123
- { goal: "大规模修改", scope: scopeFiles(15), configuredAgents: configured },
124
- { goal: "单文件", scope: ["a.ts"], configuredAgents: configured },
125
- { goal: "审计任务", configuredAgents: configured },
126
- { goal: "重构模块", configuredAgents: configured },
127
- { goal: "更新文档", configuredAgents: configured },
128
- { goal: "普通任务", scope: ["a.ts", "b.ts"], configuredAgents: configured },
129
- ];
130
- for (const input of inputs) {
131
- const result = routeAgent(input);
132
- assert.ok(configured.includes(result.recommended_agent), `recommended_agent "${result.recommended_agent}" not in configuredAgents [${configured.join(", ")}] for goal "${input.goal}"`);
133
- }
134
- });
135
- it("白名单约束:opencode 未配置时大规模 scope 回退到第一个 agent", () => {
136
- const configured = ["codex"];
137
- const result = routeAgent({
138
- goal: "大规模修改",
139
- scope: scopeFiles(15),
140
- configuredAgents: configured,
141
- });
142
- assert.equal(result.recommended_agent, "codex");
143
- assert.equal(result.fallback, true);
144
- assert.ok(result.reason.includes("fallback"), `reason should contain 'fallback', got: ${result.reason}`);
145
- assert.ok(configured.includes(result.recommended_agent));
146
- });
147
- it("白名单约束:patchwarden-direct 未配置时单文件 scope 回退", () => {
148
- const configured = ["codex", "opencode"];
149
- const result = routeAgent({
150
- goal: "修复 bug",
151
- scope: ["a.ts"],
152
- configuredAgents: configured,
153
- });
154
- assert.equal(result.recommended_agent, "codex");
155
- assert.equal(result.fallback, true);
156
- assert.ok(configured.includes(result.recommended_agent));
157
- });
158
- it("白名单约束:patchwarden-audit 未配置时审计任务回退", () => {
159
- const configured = ["codex", "opencode"];
160
- const result = routeAgent({
161
- goal: "审计任务",
162
- configuredAgents: configured,
163
- });
164
- assert.equal(result.recommended_agent, "codex");
165
- assert.equal(result.fallback, true);
166
- assert.ok(configured.includes(result.recommended_agent));
167
- });
168
- it("白名单约束:codex 未配置时重构任务回退", () => {
169
- const configured = ["opencode"];
170
- const result = routeAgent({
171
- goal: "跨模块重构",
172
- configuredAgents: configured,
173
- });
174
- assert.equal(result.recommended_agent, "opencode");
175
- assert.equal(result.fallback, true);
176
- assert.ok(configured.includes(result.recommended_agent));
177
- });
178
- // ── 8. configuredAgents 为空 → 抛错 ────────────────────────────
179
- it("configuredAgents 为空 → 抛 PatchWardenError(no_agent_configured)", () => {
180
- assert.throws(() => routeAgent({ goal: "任意任务", configuredAgents: [] }), (err) => {
181
- assert.ok(err instanceof PatchWardenError, "err should be PatchWardenError");
182
- assert.equal(err.reason, "no_agent_configured");
183
- return true;
184
- });
185
- });
186
- // ── 9. 路由规则优先级验证 ───────────────────────────────────────
187
- it("优先级:scope > 10 优先于文档关键词", () => {
188
- // scope 15 个文件 + goal 含文档关键词 → opencode(规则1 优先于规则5)
189
- const result = routeAgent({
190
- goal: "更新文档",
191
- scope: scopeFiles(15),
192
- configuredAgents: ALL_AGENTS,
193
- });
194
- assert.equal(result.recommended_agent, "opencode");
195
- assert.equal(result.fallback, false);
196
- });
197
- it("优先级:scope === 1 优先于审计关键词", () => {
198
- // scope 1 个文件 + goal 含审计关键词 → patchwarden-direct(规则2 优先于规则3)
199
- const result = routeAgent({
200
- goal: "审计任务",
201
- scope: ["a.ts"],
202
- configuredAgents: ALL_AGENTS,
203
- });
204
- assert.equal(result.recommended_agent, "patchwarden-direct");
205
- assert.equal(result.fallback, false);
206
- });
207
- it("优先级:审计关键词优先于重构关键词", () => {
208
- // goal 同时含审计和重构关键词 → patchwarden-audit(规则3 优先于规则4)
209
- const result = routeAgent({
210
- goal: "审计重构模块",
211
- configuredAgents: ALL_AGENTS,
212
- });
213
- assert.equal(result.recommended_agent, "patchwarden-audit");
214
- assert.equal(result.fallback, false);
215
- });
216
- it("优先级:重构关键词优先于文档关键词", () => {
217
- // goal 同时含重构和文档关键词 → codex(规则4 优先于规则5)
218
- const result = routeAgent({
219
- goal: "重构文档模块",
220
- configuredAgents: ALL_AGENTS,
221
- });
222
- assert.equal(result.recommended_agent, "codex");
223
- assert.equal(result.fallback, false);
224
- });
225
- // ── 10. inline_plan 文本特征匹配 ────────────────────────────────
226
- it("inline_plan 含重构关键词 → codex", () => {
227
- const result = routeAgent({
228
- goal: "改进代码",
229
- inline_plan: "需要 refactor 数据访问层",
230
- configuredAgents: ALL_AGENTS,
231
- });
232
- assert.equal(result.recommended_agent, "codex");
233
- assert.equal(result.fallback, false);
234
- });
235
- it("inline_plan 含审计关键词 → patchwarden-audit", () => {
236
- const result = routeAgent({
237
- goal: "检查工作",
238
- inline_plan: "执行 audit 验收流程",
239
- configuredAgents: ALL_AGENTS,
240
- });
241
- assert.equal(result.recommended_agent, "patchwarden-audit");
242
- assert.equal(result.fallback, false);
243
- });
244
- // ── 11. AGENT_ROUTING_RULES 常量结构断言 ────────────────────────
245
- it("AGENT_ROUTING_RULES 导出完整路由规则结构", () => {
246
- assert.equal(AGENT_ROUTING_RULES.largeScope.agent, "opencode");
247
- assert.equal(AGENT_ROUTING_RULES.largeScope.threshold, 10);
248
- assert.equal(AGENT_ROUTING_RULES.singleFile.agent, "patchwarden-direct");
249
- assert.equal(AGENT_ROUTING_RULES.audit.agent, "patchwarden-audit");
250
- assert.ok(AGENT_ROUTING_RULES.audit.keywords.includes("审计"));
251
- assert.ok(AGENT_ROUTING_RULES.audit.keywords.includes("audit"));
252
- assert.ok(AGENT_ROUTING_RULES.audit.keywords.includes("验收"));
253
- assert.ok(AGENT_ROUTING_RULES.audit.keywords.includes("verify"));
254
- assert.equal(AGENT_ROUTING_RULES.refactor.agent, "codex");
255
- assert.ok(AGENT_ROUTING_RULES.refactor.keywords.includes("重构"));
256
- assert.ok(AGENT_ROUTING_RULES.refactor.keywords.includes("refactor"));
257
- assert.ok(AGENT_ROUTING_RULES.refactor.keywords.includes("跨模块"));
258
- assert.ok(AGENT_ROUTING_RULES.refactor.keywords.includes("redesign"));
259
- assert.equal(AGENT_ROUTING_RULES.documentation.agent, "claude");
260
- assert.equal(AGENT_ROUTING_RULES.documentation.fallbackAgent, "opencode");
261
- assert.ok(AGENT_ROUTING_RULES.documentation.keywords.includes("文档"));
262
- assert.ok(AGENT_ROUTING_RULES.documentation.keywords.includes("readme"));
263
- assert.ok(AGENT_ROUTING_RULES.documentation.keywords.includes("changelog"));
264
- assert.ok(AGENT_ROUTING_RULES.documentation.keywords.includes("doc"));
265
- });
266
- // ── 12. scope 边界值测试 ────────────────────────────────────────
267
- it("scope 正好 10 个文件(threshold 边界)→ 不触发 large scope 规则", () => {
268
- // threshold 是 10,> 10 才触发,所以 10 个文件不触发规则1
269
- const result = routeAgent({
270
- goal: "普通任务",
271
- scope: scopeFiles(10),
272
- configuredAgents: ALL_AGENTS,
273
- });
274
- // 10 个文件不触发规则1,也不触发规则2(不是1个),无关键词 → fallback
275
- assert.equal(result.fallback, true);
276
- assert.equal(result.recommended_agent, ALL_AGENTS[0]);
277
- });
278
- it("scope 正好 11 个文件 → 触发 large scope 规则", () => {
279
- const result = routeAgent({
280
- goal: "普通任务",
281
- scope: scopeFiles(11),
282
- configuredAgents: ALL_AGENTS,
283
- });
284
- assert.equal(result.recommended_agent, "opencode");
285
- assert.equal(result.fallback, false);
286
- });
287
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,118 +0,0 @@
1
- import { describe, it, beforeEach, afterEach } from "node:test";
2
- import { strict as assert } from "node:assert";
3
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
4
- import { join } from "node:path";
5
- import { tmpdir } from "node:os";
6
- import { diagnoseAndroidBuild, } from "../../tools/androidDoctor.js";
7
- const VALID_STATUSES = ["ok", "warn", "fail", "skip"];
8
- /** Restore an env var to its original value (deleted when undefined). */
9
- function restoreEnv(key, value) {
10
- if (value === undefined)
11
- delete process.env[key];
12
- else
13
- process.env[key] = value;
14
- }
15
- describe("diagnoseAndroidBuild", () => {
16
- let tempDir;
17
- let savedJavaHome;
18
- let savedAndroidHome;
19
- let savedAndroidSdkRoot;
20
- beforeEach(() => {
21
- tempDir = mkdtempSync(join(tmpdir(), "pw-android-doctor-"));
22
- savedJavaHome = process.env.JAVA_HOME;
23
- savedAndroidHome = process.env.ANDROID_HOME;
24
- savedAndroidSdkRoot = process.env.ANDROID_SDK_ROOT;
25
- });
26
- afterEach(() => {
27
- rmSync(tempDir, { recursive: true, force: true });
28
- restoreEnv("JAVA_HOME", savedJavaHome);
29
- restoreEnv("ANDROID_HOME", savedAndroidHome);
30
- restoreEnv("ANDROID_SDK_ROOT", savedAndroidSdkRoot);
31
- });
32
- it("returns a skip result when no android_app directory exists", () => {
33
- const result = diagnoseAndroidBuild(tempDir);
34
- assert.equal(result.status, "skip");
35
- assert.equal(result.reason, "No android_app directory found");
36
- });
37
- it("returns a structured diagnostic report when android_app exists", () => {
38
- const androidApp = join(tempDir, "android_app");
39
- mkdirSync(join(androidApp, "app"), { recursive: true });
40
- mkdirSync(join(androidApp, "gradle", "wrapper"), { recursive: true });
41
- writeFileSync(join(androidApp, "gradlew"), "#!/bin/sh\necho gradle\n");
42
- writeFileSync(join(androidApp, "settings.gradle.kts"), 'pluginManagement { repositories { google() } }\n');
43
- writeFileSync(join(androidApp, "app", "build.gradle.kts"), 'plugins { id("com.android.application") }\n');
44
- writeFileSync(join(androidApp, "gradle", "wrapper", "gradle-wrapper.properties"), "distributionUrl=https://services.gradle.org/distributions/gradle-8.5-bin.zip\n");
45
- // Make SDK-related checks deterministic regardless of the host environment.
46
- delete process.env.ANDROID_HOME;
47
- delete process.env.ANDROID_SDK_ROOT;
48
- const result = diagnoseAndroidBuild(tempDir);
49
- assert.notEqual(result.status, "skip");
50
- const report = result;
51
- assert.equal(report.repo_path, tempDir);
52
- assert.equal(report.android_app_path, join(tempDir, "android_app"));
53
- assert.ok(Array.isArray(report.checks));
54
- assert.equal(report.checks.length, 10);
55
- for (const item of report.checks) {
56
- assert.equal(typeof item.check, "string");
57
- assert.ok(item.check.length > 0);
58
- assert.ok(VALID_STATUSES.includes(item.status));
59
- assert.equal(typeof item.reason, "string");
60
- assert.equal(typeof item.suggested_fix, "string");
61
- }
62
- // Overall status follows the fail > warn > ok precedence.
63
- const hasFail = report.checks.some((c) => c.status === "fail");
64
- const hasWarn = report.checks.some((c) => c.status === "warn");
65
- const expected = hasFail ? "fail" : hasWarn ? "warn" : "ok";
66
- assert.equal(report.status, expected);
67
- // The expected check names are all present.
68
- const checkNames = report.checks.map((c) => c.check);
69
- assert.ok(checkNames.includes("java -version"));
70
- assert.ok(checkNames.includes("JAVA_HOME"));
71
- assert.ok(checkNames.includes("ANDROID_HOME"));
72
- assert.ok(checkNames.includes("ANDROID_SDK_ROOT"));
73
- assert.ok(checkNames.includes("Android SDK platform"));
74
- assert.ok(checkNames.includes("Android build-tools"));
75
- assert.ok(checkNames.includes("Gradle wrapper"));
76
- assert.ok(checkNames.includes("android_app/settings.gradle.kts"));
77
- assert.ok(checkNames.includes("android_app/app/build.gradle.kts"));
78
- assert.ok(checkNames.includes("APK output path"));
79
- // Files we created should be detected as ok.
80
- const gradleCheck = report.checks.find((c) => c.check === "Gradle wrapper");
81
- assert.equal(gradleCheck?.status, "ok");
82
- const settingsCheck = report.checks.find((c) => c.check === "android_app/settings.gradle.kts");
83
- assert.equal(settingsCheck?.status, "ok");
84
- const appBuildCheck = report.checks.find((c) => c.check === "android_app/app/build.gradle.kts");
85
- assert.equal(appBuildCheck?.status, "ok");
86
- });
87
- it("reports a missing JAVA_HOME as warn or fail", () => {
88
- const androidApp = join(tempDir, "android_app");
89
- mkdirSync(androidApp, { recursive: true });
90
- delete process.env.JAVA_HOME;
91
- delete process.env.ANDROID_HOME;
92
- delete process.env.ANDROID_SDK_ROOT;
93
- const result = diagnoseAndroidBuild(tempDir);
94
- const report = result;
95
- const javaHomeCheck = report.checks.find((c) => c.check === "JAVA_HOME");
96
- assert.ok(javaHomeCheck, "JAVA_HOME check should exist");
97
- assert.ok(javaHomeCheck.status === "warn" || javaHomeCheck.status === "fail", `expected warn or fail, got ${javaHomeCheck.status}`);
98
- });
99
- it("reports a missing ANDROID_HOME as fail with the SDK-missing message", () => {
100
- const androidApp = join(tempDir, "android_app");
101
- mkdirSync(androidApp, { recursive: true });
102
- delete process.env.ANDROID_HOME;
103
- delete process.env.ANDROID_SDK_ROOT;
104
- const result = diagnoseAndroidBuild(tempDir);
105
- const report = result;
106
- const androidHomeCheck = report.checks.find((c) => c.check === "ANDROID_HOME");
107
- assert.ok(androidHomeCheck, "ANDROID_HOME check should exist");
108
- assert.equal(androidHomeCheck.status, "fail");
109
- assert.ok(androidHomeCheck.reason.includes("Android project exists, APK not built because Android SDK is missing."), `reason should mention the SDK-missing sentence, got: ${androidHomeCheck.reason}`);
110
- // With the SDK missing, the overall status must be fail.
111
- assert.equal(report.status, "fail");
112
- // The APK output check should also carry the SDK-missing reason and be skipped.
113
- const apkCheck = report.checks.find((c) => c.check === "APK output path");
114
- assert.ok(apkCheck, "APK output path check should exist");
115
- assert.equal(apkCheck.status, "skip");
116
- assert.ok(apkCheck.reason.includes("Android project exists, APK not built because Android SDK is missing."));
117
- });
118
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,225 +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, readFileSync } from "node:fs";
4
- import { join } from "node:path";
5
- import { tmpdir } from "node:os";
6
- import { createHash } from "node:crypto";
7
- import { applyPatch } from "../../tools/applyPatch.js";
8
- import { createDirectSession } from "../../direct/directSessionStore.js";
9
- import { reloadConfig } from "../../config.js";
10
- import { PatchWardenError } from "../../errors.js";
11
- // ── Helpers ────────────────────────────────────────────────────────
12
- function sha256(content) {
13
- return createHash("sha256").update(content, "utf-8").digest("hex");
14
- }
15
- function makeSnapshot() {
16
- return {
17
- captured_at: new Date().toISOString(),
18
- is_git: false,
19
- head: null,
20
- status: "",
21
- workspace_dirty: false,
22
- files: {},
23
- dirty_paths: [],
24
- warnings: [],
25
- };
26
- }
27
- function bootstrapWorkspace(prefix, maxPatchBytes) {
28
- const tempDir = mkdtempSync(join(tmpdir(), prefix));
29
- const repoPath = join(tempDir, "my-repo");
30
- mkdirSync(repoPath, { recursive: true });
31
- mkdirSync(join(repoPath, "src"), { recursive: true });
32
- const configPath = join(tempDir, "patchwarden.config.json");
33
- writeFileSync(configPath, JSON.stringify({
34
- workspaceRoot: tempDir,
35
- agents: { codex: { command: "codex", args: ["exec", "{prompt}"] } },
36
- allowedTestCommands: ["npm test"],
37
- directMaxPatchBytes: maxPatchBytes,
38
- directMaxFileBytes: 500_000,
39
- }), "utf-8");
40
- process.env.PATCHWARDEN_CONFIG = configPath;
41
- reloadConfig();
42
- const session = createDirectSession({
43
- repo_path: "my-repo",
44
- resolved_repo_path: repoPath,
45
- title: "applyPatch test",
46
- snapshot: makeSnapshot(),
47
- });
48
- return { tempDir, repoPath, sessionId: session.session_id };
49
- }
50
- function teardownWorkspace(tempDir) {
51
- rmSync(tempDir, { recursive: true, force: true });
52
- delete process.env.PATCHWARDEN_CONFIG;
53
- reloadConfig();
54
- }
55
- // ── Success cases ──────────────────────────────────────────────────
56
- describe("applyPatch success cases", () => {
57
- let tempDir;
58
- let repoPath;
59
- let sessionId;
60
- beforeEach(() => {
61
- const ws = bootstrapWorkspace("pw-applypatch-ok-", 200_000);
62
- tempDir = ws.tempDir;
63
- repoPath = ws.repoPath;
64
- sessionId = ws.sessionId;
65
- });
66
- afterEach(() => {
67
- teardownWorkspace(tempDir);
68
- });
69
- it("applies replace_exact operation", () => {
70
- const filePath = join(repoPath, "src", "main.ts");
71
- const original = "hello world";
72
- writeFileSync(filePath, original, "utf-8");
73
- const operations = [
74
- { type: "replace_exact", old_text: "world", new_text: "there" },
75
- ];
76
- const result = applyPatch({
77
- session_id: sessionId,
78
- path: "src/main.ts",
79
- expected_sha256: sha256(original),
80
- operations,
81
- });
82
- assert.equal(result.path, "src/main.ts");
83
- assert.equal(result.before_sha256, sha256(original));
84
- assert.equal(result.after_sha256, sha256("hello there"));
85
- assert.equal(result.operations_applied, 1);
86
- assert.equal(result.bytes_changed, 0);
87
- assert.ok(result.next_action.length > 0);
88
- assert.equal(readFileSync(filePath, "utf-8"), "hello there");
89
- });
90
- it("applies insert_before operation", () => {
91
- const filePath = join(repoPath, "src", "main.ts");
92
- const original = "hello world";
93
- writeFileSync(filePath, original, "utf-8");
94
- const operations = [
95
- { type: "insert_before", old_text: "world", new_text: "beautiful " },
96
- ];
97
- const result = applyPatch({
98
- session_id: sessionId,
99
- path: "src/main.ts",
100
- expected_sha256: sha256(original),
101
- operations,
102
- });
103
- assert.equal(readFileSync(filePath, "utf-8"), "hello beautiful world");
104
- assert.equal(result.after_sha256, sha256("hello beautiful world"));
105
- assert.equal(result.operations_applied, 1);
106
- assert.equal(result.bytes_changed, 10);
107
- });
108
- it("applies insert_after operation", () => {
109
- const filePath = join(repoPath, "src", "main.ts");
110
- const original = "hello world";
111
- writeFileSync(filePath, original, "utf-8");
112
- const operations = [
113
- { type: "insert_after", old_text: "hello", new_text: " beautiful" },
114
- ];
115
- const result = applyPatch({
116
- session_id: sessionId,
117
- path: "src/main.ts",
118
- expected_sha256: sha256(original),
119
- operations,
120
- });
121
- assert.equal(readFileSync(filePath, "utf-8"), "hello beautiful world");
122
- assert.equal(result.after_sha256, sha256("hello beautiful world"));
123
- assert.equal(result.operations_applied, 1);
124
- assert.equal(result.bytes_changed, 10);
125
- });
126
- it("applies replace_whole_file operation", () => {
127
- const filePath = join(repoPath, "src", "main.ts");
128
- const original = "hello world";
129
- writeFileSync(filePath, original, "utf-8");
130
- const newContent = "completely new content";
131
- const operations = [
132
- { type: "replace_whole_file", new_text: newContent },
133
- ];
134
- const result = applyPatch({
135
- session_id: sessionId,
136
- path: "src/main.ts",
137
- expected_sha256: sha256(original),
138
- operations,
139
- });
140
- assert.equal(readFileSync(filePath, "utf-8"), newContent);
141
- assert.equal(result.after_sha256, sha256(newContent));
142
- assert.equal(result.operations_applied, 1);
143
- assert.equal(result.bytes_changed, 11);
144
- });
145
- });
146
- // ── Rejection cases ────────────────────────────────────────────────
147
- describe("applyPatch rejection cases", () => {
148
- let tempDir;
149
- let repoPath;
150
- let sessionId;
151
- beforeEach(() => {
152
- const ws = bootstrapWorkspace("pw-applypatch-rej-", 200_000);
153
- tempDir = ws.tempDir;
154
- repoPath = ws.repoPath;
155
- sessionId = ws.sessionId;
156
- });
157
- afterEach(() => {
158
- teardownWorkspace(tempDir);
159
- });
160
- it("rejects when expected_sha256 does not match", () => {
161
- const filePath = join(repoPath, "src", "main.ts");
162
- writeFileSync(filePath, "hello world", "utf-8");
163
- assert.throws(() => applyPatch({
164
- session_id: sessionId,
165
- path: "src/main.ts",
166
- expected_sha256: "0".repeat(64),
167
- operations: [{ type: "replace_whole_file", new_text: "new" }],
168
- }), (err) => err instanceof PatchWardenError && err.reason === "file_hash_mismatch");
169
- // File content must remain unchanged after rejection
170
- assert.equal(readFileSync(filePath, "utf-8"), "hello world");
171
- });
172
- it("rejects binary files (e.g. .png)", () => {
173
- const filePath = join(repoPath, "src", "image.png");
174
- writeFileSync(filePath, "fake png content", "utf-8");
175
- assert.throws(() => applyPatch({
176
- session_id: sessionId,
177
- path: "src/image.png",
178
- expected_sha256: sha256("fake png content"),
179
- operations: [{ type: "replace_whole_file", new_text: "new" }],
180
- }), (err) => err instanceof PatchWardenError && err.reason === "binary_file_blocked");
181
- // File content must remain unchanged after rejection
182
- assert.equal(readFileSync(filePath, "utf-8"), "fake png content");
183
- });
184
- it("rejects paths outside workspaceRoot", () => {
185
- assert.throws(() => applyPatch({
186
- session_id: sessionId,
187
- path: "../../../etc/passwd",
188
- expected_sha256: sha256("dummy"),
189
- operations: [{ type: "replace_whole_file", new_text: "new" }],
190
- }), PatchWardenError);
191
- });
192
- });
193
- // ── Patch size limit ───────────────────────────────────────────────
194
- describe("applyPatch patch size limit", () => {
195
- let tempDir;
196
- let repoPath;
197
- let sessionId;
198
- beforeEach(() => {
199
- const ws = bootstrapWorkspace("pw-applypatch-size-", 100);
200
- tempDir = ws.tempDir;
201
- repoPath = ws.repoPath;
202
- sessionId = ws.sessionId;
203
- });
204
- afterEach(() => {
205
- teardownWorkspace(tempDir);
206
- });
207
- it("rejects patches exceeding directMaxPatchBytes", () => {
208
- const filePath = join(repoPath, "src", "main.ts");
209
- writeFileSync(filePath, "hello world", "utf-8");
210
- // Build a patch whose JSON.stringify length exceeds the 100-byte limit
211
- const bigText = "x".repeat(80);
212
- const operations = [
213
- { type: "replace_whole_file", new_text: bigText },
214
- ];
215
- assert.ok(JSON.stringify(operations).length > 100);
216
- assert.throws(() => applyPatch({
217
- session_id: sessionId,
218
- path: "src/main.ts",
219
- expected_sha256: sha256("hello world"),
220
- operations,
221
- }), (err) => err instanceof PatchWardenError && err.reason === "patch_too_large");
222
- // File content must remain unchanged after rejection
223
- assert.equal(readFileSync(filePath, "utf-8"), "hello world");
224
- });
225
- });
@@ -1 +0,0 @@
1
- export {};