patchwarden 1.5.1 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (591) hide show
  1. package/PatchWarden.cmd +32 -2
  2. package/README.en.md +158 -26
  3. package/README.md +124 -24
  4. package/dist/assessments/agentAssessor.js +21 -12
  5. package/dist/assessments/assessmentDiagnostics.d.ts +29 -0
  6. package/dist/assessments/assessmentDiagnostics.js +132 -0
  7. package/dist/assessments/assessmentStore.d.ts +39 -0
  8. package/dist/assessments/assessmentStore.js +207 -45
  9. package/dist/assessments/securitySnapshot.d.ts +50 -0
  10. package/dist/assessments/securitySnapshot.js +158 -0
  11. package/dist/config.d.ts +15 -0
  12. package/dist/config.js +66 -13
  13. package/dist/control/fileManager.d.ts +2 -0
  14. package/dist/control/fileManager.js +39 -0
  15. package/dist/control/middleware/auth.d.ts +6 -0
  16. package/dist/control/middleware/auth.js +14 -5
  17. package/dist/control/middleware/static.js +16 -3
  18. package/dist/control/routeTable.d.ts +24 -0
  19. package/dist/control/routeTable.js +263 -0
  20. package/dist/control/routes/audit.js +17 -7
  21. package/dist/control/routes/evidence.js +1 -1
  22. package/dist/control/routes/lineage.js +1 -1
  23. package/dist/control/routes/process.d.ts +7 -0
  24. package/dist/control/routes/process.js +329 -42
  25. package/dist/control/routes/sessions.d.ts +9 -1
  26. package/dist/control/routes/sessions.js +84 -21
  27. package/dist/control/routes/status.d.ts +60 -1
  28. package/dist/control/routes/status.js +94 -15
  29. package/dist/control/routes/taskActions.js +24 -60
  30. package/dist/control/routes/tasks.d.ts +4 -0
  31. package/dist/control/routes/tasks.js +61 -62
  32. package/dist/control/routes/workspace.js +50 -23
  33. package/dist/control/runtime.d.ts +3 -2
  34. package/dist/control/runtime.js +65 -40
  35. package/dist/control/server.js +37 -365
  36. package/dist/control/shared.d.ts +25 -1
  37. package/dist/control/shared.js +64 -11
  38. package/dist/diagnostics/allowedTestCommandSafety.d.ts +1 -0
  39. package/dist/diagnostics/allowedTestCommandSafety.js +11 -0
  40. package/dist/direct/directAudit.js +234 -225
  41. package/dist/direct/directGuards.d.ts +2 -1
  42. package/dist/direct/directGuards.js +42 -29
  43. package/dist/direct/directPatch.d.ts +6 -1
  44. package/dist/direct/directPatch.js +55 -8
  45. package/dist/direct/directSessionStore.d.ts +16 -6
  46. package/dist/direct/directSessionStore.js +128 -35
  47. package/dist/direct/directVerification.js +8 -16
  48. package/dist/doctor.js +65 -57
  49. package/dist/goal/acceptanceEngine.d.ts +1 -1
  50. package/dist/goal/goalProgress.js +75 -69
  51. package/dist/goal/goalReport.js +5 -12
  52. package/dist/goal/goalStatus.d.ts +2 -0
  53. package/dist/goal/goalStatus.js +10 -3
  54. package/dist/goal/goalStore.d.ts +9 -2
  55. package/dist/goal/goalStore.js +152 -45
  56. package/dist/goal/handoffExport.js +6 -6
  57. package/dist/goal/specKitImport.js +97 -96
  58. package/dist/goal/subgoalSync.js +11 -9
  59. package/dist/goal/worktreeManager.d.ts +2 -0
  60. package/dist/goal/worktreeManager.js +209 -136
  61. package/dist/httpServer.js +111 -32
  62. package/dist/logging.js +5 -3
  63. package/dist/policy/projectPolicy.js +5 -2
  64. package/dist/release/releaseGate.d.ts +1 -1
  65. package/dist/release/releaseGate.js +78 -25
  66. package/dist/runner/agentInvocation.d.ts +14 -0
  67. package/dist/runner/agentInvocation.js +96 -4
  68. package/dist/runner/changeCapture.d.ts +13 -5
  69. package/dist/runner/changeCapture.js +243 -61
  70. package/dist/runner/postTaskCleanup.js +48 -19
  71. package/dist/runner/processSecurity.d.ts +56 -0
  72. package/dist/runner/processSecurity.js +373 -0
  73. package/dist/runner/runTask.d.ts +2 -1
  74. package/dist/runner/runTask.js +162 -93
  75. package/dist/runner/simpleProcess.d.ts +3 -0
  76. package/dist/runner/simpleProcess.js +50 -36
  77. package/dist/{taskProgress.d.ts → runner/taskProgress.d.ts} +1 -1
  78. package/dist/{taskProgress.js → runner/taskProgress.js} +2 -2
  79. package/dist/{taskRuntime.d.ts → runner/taskRuntime.d.ts} +3 -1
  80. package/dist/{taskRuntime.js → runner/taskRuntime.js} +3 -2
  81. package/dist/runner/taskStatusStore.d.ts +11 -0
  82. package/dist/runner/taskStatusStore.js +35 -0
  83. package/dist/runner/watch.d.ts +14 -1
  84. package/dist/runner/watch.js +320 -49
  85. package/dist/security/contentRedaction.js +2 -2
  86. package/dist/security/discoveryTokenStore.d.ts +3 -2
  87. package/dist/security/discoveryTokenStore.js +17 -2
  88. package/dist/security/loopbackHost.d.ts +2 -0
  89. package/dist/security/loopbackHost.js +16 -0
  90. package/dist/security/pathGuard.js +32 -17
  91. package/dist/security/planGuard.js +2 -1
  92. package/dist/security/runtimeGuard.js +9 -6
  93. package/dist/security/secretComparison.d.ts +3 -0
  94. package/dist/security/secretComparison.js +11 -0
  95. package/dist/security/sensitiveGuard.d.ts +5 -0
  96. package/dist/security/sensitiveGuard.js +27 -8
  97. package/dist/security/toolInvocationGuard.d.ts +2 -2
  98. package/dist/security/toolInvocationGuard.js +1 -1
  99. package/dist/security/workspaceRootGuard.d.ts +8 -0
  100. package/dist/security/workspaceRootGuard.js +47 -0
  101. package/dist/smoke-test.js +27 -26
  102. package/dist/tools/catalog/index.d.ts +4 -0
  103. package/dist/tools/catalog/index.js +4 -0
  104. package/dist/tools/{toolCatalog.js → catalog/toolCatalog.js} +3 -12
  105. package/dist/tools/{toolRegistry.d.ts → catalog/toolRegistry.d.ts} +3 -3
  106. package/dist/tools/{toolRegistry.js → catalog/toolRegistry.js} +6 -15
  107. package/dist/tools/definitions/toolDefs.d.ts +18 -0
  108. package/dist/tools/definitions/toolDefs.js +1177 -0
  109. package/dist/tools/{auditSession.d.ts → diagnostics/auditSession.d.ts} +1 -1
  110. package/dist/tools/{auditSession.js → diagnostics/auditSession.js} +2 -2
  111. package/dist/tools/{auditTask.d.ts → diagnostics/auditTask.d.ts} +2 -1
  112. package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +134 -36
  113. package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +18 -6
  114. package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +36 -8
  115. package/dist/tools/diagnostics/index.d.ts +6 -0
  116. package/dist/tools/diagnostics/index.js +6 -0
  117. package/dist/tools/{safeStatus.d.ts → diagnostics/safeStatus.d.ts} +3 -3
  118. package/dist/tools/{safeStatus.js → diagnostics/safeStatus.js} +6 -6
  119. package/dist/tools/{safeViews.d.ts → diagnostics/safeViews.d.ts} +12 -8
  120. package/dist/tools/{safeViews.js → diagnostics/safeViews.js} +23 -13
  121. package/dist/tools/{schemaDriftCheck.d.ts → diagnostics/schemaDriftCheck.d.ts} +1 -1
  122. package/dist/tools/{schemaDriftCheck.js → diagnostics/schemaDriftCheck.js} +2 -2
  123. package/dist/tools/{createDirectSession.js → direct/createDirectSession.js} +6 -6
  124. package/dist/tools/{finalizeDirectSession.d.ts → direct/finalizeDirectSession.d.ts} +1 -1
  125. package/dist/tools/direct/finalizeDirectSession.js +86 -0
  126. package/dist/tools/direct/index.d.ts +3 -0
  127. package/dist/tools/direct/index.js +3 -0
  128. package/dist/tools/{runDirectVerificationBundle.js → direct/runDirectVerificationBundle.js} +2 -2
  129. package/dist/tools/{discoverTools.d.ts → discovery/discoverTools.d.ts} +2 -2
  130. package/dist/tools/{discoverTools.js → discovery/discoverTools.js} +5 -5
  131. package/dist/tools/{explainTool.d.ts → discovery/explainTool.d.ts} +2 -2
  132. package/dist/tools/{explainTool.js → discovery/explainTool.js} +2 -2
  133. package/dist/tools/discovery/index.d.ts +3 -0
  134. package/dist/tools/discovery/index.js +3 -0
  135. package/dist/tools/{invokeDiscoveredTool.d.ts → discovery/invokeDiscoveredTool.d.ts} +2 -2
  136. package/dist/tools/{invokeDiscoveredTool.js → discovery/invokeDiscoveredTool.js} +5 -5
  137. package/dist/tools/dispatch/coreDispatch.js +32 -31
  138. package/dist/tools/dispatch/diagnosticDispatch.d.ts +3 -5
  139. package/dist/tools/dispatch/diagnosticDispatch.js +70 -68
  140. package/dist/tools/dispatch/directDispatch.js +11 -10
  141. package/dist/tools/dispatch/goalDispatch.js +5 -4
  142. package/dist/tools/dispatch/releaseDispatch.js +3 -2
  143. package/dist/tools/dispatch/validation.d.ts +7 -0
  144. package/dist/tools/dispatch/validation.js +71 -0
  145. package/dist/tools/{getPlan.js → goals/getPlan.js} +4 -4
  146. package/dist/tools/{goalSubgoalTask.d.ts → goals/goalSubgoalTask.d.ts} +1 -1
  147. package/dist/tools/goals/goalSubgoalTask.js +118 -0
  148. package/dist/tools/goals/index.d.ts +3 -0
  149. package/dist/tools/goals/index.js +3 -0
  150. package/dist/tools/{savePlan.js → goals/savePlan.js} +28 -8
  151. package/dist/tools/registry.d.ts +7 -11
  152. package/dist/tools/registry.js +17 -1178
  153. package/dist/tools/{checkReleaseGate.d.ts → release/checkReleaseGate.d.ts} +1 -1
  154. package/dist/tools/{checkReleaseGate.js → release/checkReleaseGate.js} +3 -3
  155. package/dist/tools/release/index.d.ts +2 -0
  156. package/dist/tools/release/index.js +2 -0
  157. package/dist/tools/{releaseMode.d.ts → release/releaseMode.d.ts} +2 -2
  158. package/dist/tools/{releaseMode.js → release/releaseMode.js} +46 -26
  159. package/dist/tools/tasks/cancelTask.d.ts +11 -0
  160. package/dist/tools/tasks/cancelTask.js +86 -0
  161. package/dist/tools/{createTask.d.ts → tasks/createTask.d.ts} +12 -4
  162. package/dist/tools/{createTask.js → tasks/createTask.js} +110 -34
  163. package/dist/tools/{diagnoseTask.d.ts → tasks/diagnoseTask.d.ts} +1 -1
  164. package/dist/tools/{diagnoseTask.js → tasks/diagnoseTask.js} +12 -16
  165. package/dist/tools/{evidencePack.js → tasks/evidencePack.js} +34 -22
  166. package/dist/tools/{getTaskFile.js → tasks/getTaskFile.js} +4 -4
  167. package/dist/tools/{getTaskStatus.d.ts → tasks/getTaskStatus.d.ts} +6 -1
  168. package/dist/tools/{getTaskStatus.js → tasks/getTaskStatus.js} +11 -6
  169. package/dist/tools/{getTaskStdoutTail.js → tasks/getTaskStdoutTail.js} +13 -11
  170. package/dist/tools/{getTaskSummary.js → tasks/getTaskSummary.js} +35 -36
  171. package/dist/tools/tasks/index.d.ts +18 -0
  172. package/dist/tools/tasks/index.js +18 -0
  173. package/dist/tools/tasks/killTask.d.ts +1 -0
  174. package/dist/tools/{listTasks.d.ts → tasks/listTasks.d.ts} +1 -1
  175. package/dist/tools/{listTasks.js → tasks/listTasks.js} +5 -4
  176. package/dist/tools/{reconcileTasks.d.ts → tasks/reconcileTasks.d.ts} +3 -2
  177. package/dist/tools/tasks/reconcileTasks.js +496 -0
  178. package/dist/tools/tasks/retryTask.js +60 -0
  179. package/dist/tools/{runTaskLoop.d.ts → tasks/runTaskLoop.d.ts} +6 -6
  180. package/dist/tools/{runTaskLoop.js → tasks/runTaskLoop.js} +63 -41
  181. package/dist/tools/tasks/runVerification.js +38 -0
  182. package/dist/tools/{taskLineage.js → tasks/taskLineage.js} +8 -7
  183. package/dist/tools/{taskOutputs.d.ts → tasks/taskOutputs.d.ts} +2 -2
  184. package/dist/tools/{taskOutputs.js → tasks/taskOutputs.js} +17 -16
  185. package/dist/tools/tasks/taskStates.d.ts +6 -0
  186. package/dist/tools/tasks/taskStates.js +52 -0
  187. package/dist/tools/{waitForTask.js → tasks/waitForTask.js} +3 -11
  188. package/dist/tools/{androidDoctor.js → workspace/androidDoctor.js} +16 -10
  189. package/dist/tools/{applyPatch.d.ts → workspace/applyPatch.d.ts} +1 -1
  190. package/dist/tools/workspace/applyPatch.js +53 -0
  191. package/dist/tools/{discardWorktree.js → workspace/discardWorktree.js} +3 -3
  192. package/dist/tools/workspace/index.d.ts +10 -0
  193. package/dist/tools/workspace/index.js +10 -0
  194. package/dist/tools/workspace/listAgents.d.ts +23 -0
  195. package/dist/tools/workspace/listAgents.js +56 -0
  196. package/dist/tools/{listWorkspace.js → workspace/listWorkspace.js} +3 -3
  197. package/dist/tools/{mergeWorktree.js → workspace/mergeWorktree.js} +3 -3
  198. package/dist/tools/{readWorkspaceFile.js → workspace/readWorkspaceFile.js} +8 -8
  199. package/dist/tools/{recommendAgentForTask.js → workspace/recommendAgentForTask.js} +3 -3
  200. package/dist/tools/{searchWorkspace.js → workspace/searchWorkspace.js} +5 -5
  201. package/dist/tools/{syncFile.d.ts → workspace/syncFile.d.ts} +1 -1
  202. package/dist/tools/workspace/syncFile.js +134 -0
  203. package/dist/utils/atomicFile.d.ts +7 -0
  204. package/dist/utils/atomicFile.js +67 -0
  205. package/dist/utils/boundedFile.d.ts +8 -0
  206. package/dist/utils/boundedFile.js +93 -0
  207. package/dist/utils/lockedJsonFile.d.ts +15 -0
  208. package/dist/utils/lockedJsonFile.js +224 -0
  209. package/dist/utils/platform.d.ts +11 -0
  210. package/dist/utils/platform.js +26 -0
  211. package/dist/utils/stableJson.d.ts +2 -0
  212. package/dist/utils/stableJson.js +12 -0
  213. package/dist/version.d.ts +3 -2
  214. package/dist/version.js +3 -2
  215. package/dist/watcherStatus.d.ts +2 -2
  216. package/dist/watcherStatus.js +18 -0
  217. package/docs/CODE_WIKI.md +808 -143
  218. package/docs/architecture.md +15 -55
  219. package/docs/assets/patchwarden-desktop-onboarding.png +0 -0
  220. package/docs/control-center/README.md +4 -4
  221. package/docs/dashboard-overview.md +4 -4
  222. package/docs/desktop-app.md +175 -0
  223. package/docs/evidence-pack-schema.md +3 -3
  224. package/docs/open-source-application.md +16 -11
  225. package/docs/release-checklist.md +1 -1
  226. package/docs/release-evidence.md +35 -39
  227. package/docs/task-safe-review-workflow.md +5 -5
  228. package/docs/threat-model.md +19 -1
  229. package/examples/config.example.json +4 -2
  230. package/package.json +27 -10
  231. package/scripts/README.md +15 -6
  232. package/scripts/build.js +11 -0
  233. package/scripts/checks/brand-check.js +3 -0
  234. package/scripts/checks/build-output-check.js +37 -0
  235. package/scripts/checks/control-center-smoke.js +102 -10
  236. package/scripts/checks/control-smoke.js +19 -8
  237. package/scripts/checks/http-mcp-smoke.js +82 -4
  238. package/scripts/checks/lifecycle-smoke.js +15 -12
  239. package/scripts/checks/mcp-manifest-check.js +1 -1
  240. package/scripts/checks/mcp-smoke.js +58 -4
  241. package/scripts/checks/package-manifest-check.js +20 -7
  242. package/scripts/checks/unit-tests.js +3 -5
  243. package/scripts/checks/watcher-supervisor-smoke.js +38 -1
  244. package/scripts/control/get-patchwarden-health.ps1 +1 -1
  245. package/scripts/control/manage-patchwarden.ps1 +69 -4
  246. package/scripts/control/provision-patchwarden-tunnel.ps1 +127 -0
  247. package/scripts/control/run-background-supervisor.ps1 +41 -0
  248. package/scripts/control/start-patchwarden-tunnel.ps1 +191 -33
  249. package/scripts/e2e/demo-runtime.mjs +153 -0
  250. package/{PatchWarden-Control-Tray.cmd → scripts/launchers/PatchWarden-Control-Tray.cmd} +3 -3
  251. package/scripts/launchers/PatchWarden-Control.cmd +6 -0
  252. package/{PatchWarden-Desktop.cmd → scripts/launchers/PatchWarden-Desktop.cmd} +2 -2
  253. package/scripts/launchers/Restart-PatchWarden-Control.cmd +6 -0
  254. package/{Stop-PatchWarden.cmd → scripts/launchers/Stop-PatchWarden.cmd} +2 -2
  255. package/scripts/lib/clean-generated-output.js +11 -0
  256. package/scripts/lib/file-discovery.js +12 -0
  257. package/scripts/release/desktop-preflight.js +211 -0
  258. package/scripts/release/pack-clean.js +59 -14
  259. package/src/assessments/agentAssessor.ts +26 -12
  260. package/src/assessments/assessmentDiagnostics.ts +172 -0
  261. package/src/assessments/assessmentStore.ts +288 -51
  262. package/src/assessments/securitySnapshot.ts +231 -0
  263. package/src/config.ts +70 -13
  264. package/src/control/fileManager.ts +40 -0
  265. package/src/control/middleware/auth.ts +22 -4
  266. package/src/control/middleware/static.ts +16 -3
  267. package/src/control/routeTable.ts +301 -0
  268. package/src/control/routes/audit.ts +14 -6
  269. package/src/control/routes/evidence.ts +1 -1
  270. package/src/control/routes/lineage.ts +1 -1
  271. package/src/control/routes/process.ts +341 -40
  272. package/src/control/routes/sessions.ts +88 -25
  273. package/src/control/routes/status.ts +134 -17
  274. package/src/control/routes/taskActions.ts +24 -58
  275. package/src/control/routes/tasks.ts +66 -58
  276. package/src/control/routes/workspace.ts +56 -25
  277. package/src/control/runtime.ts +71 -43
  278. package/src/control/server.ts +37 -342
  279. package/src/control/shared.ts +77 -11
  280. package/src/diagnostics/allowedTestCommandSafety.ts +12 -0
  281. package/src/direct/directAudit.ts +17 -9
  282. package/src/direct/directGuards.ts +72 -44
  283. package/src/direct/directPatch.ts +95 -8
  284. package/src/direct/directSessionStore.ts +213 -55
  285. package/src/direct/directVerification.ts +9 -23
  286. package/src/doctor.ts +76 -64
  287. package/src/goal/acceptanceEngine.ts +1 -1
  288. package/src/goal/goalProgress.ts +17 -13
  289. package/src/goal/goalReport.ts +5 -13
  290. package/src/goal/goalStatus.ts +19 -3
  291. package/src/goal/goalStore.ts +241 -54
  292. package/src/goal/handoffExport.ts +6 -6
  293. package/src/goal/specKitImport.ts +12 -12
  294. package/src/goal/subgoalSync.ts +11 -8
  295. package/src/goal/worktreeManager.ts +139 -22
  296. package/src/httpServer.ts +113 -30
  297. package/src/logging.ts +10 -3
  298. package/src/policy/projectPolicy.ts +5 -2
  299. package/src/release/releaseGate.ts +74 -27
  300. package/src/runner/agentInvocation.ts +126 -4
  301. package/src/runner/changeCapture.ts +291 -57
  302. package/src/runner/postTaskCleanup.ts +41 -17
  303. package/src/runner/processSecurity.ts +433 -0
  304. package/src/runner/runTask.ts +197 -97
  305. package/src/runner/simpleProcess.ts +66 -31
  306. package/src/{taskProgress.ts → runner/taskProgress.ts} +3 -3
  307. package/src/{taskRuntime.ts → runner/taskRuntime.ts} +6 -3
  308. package/src/runner/taskStatusStore.ts +69 -0
  309. package/src/runner/watch.ts +348 -52
  310. package/src/security/contentRedaction.ts +2 -2
  311. package/src/security/discoveryTokenStore.ts +18 -4
  312. package/src/security/loopbackHost.ts +19 -0
  313. package/src/security/pathGuard.ts +35 -16
  314. package/src/security/planGuard.ts +2 -1
  315. package/src/security/runtimeGuard.ts +9 -6
  316. package/src/security/secretComparison.ts +13 -0
  317. package/src/security/sensitiveGuard.ts +27 -8
  318. package/src/security/toolInvocationGuard.ts +2 -2
  319. package/src/security/workspaceRootGuard.ts +53 -0
  320. package/src/smoke-test.ts +27 -26
  321. package/src/tools/catalog/index.ts +4 -0
  322. package/src/tools/{toolCatalog.ts → catalog/toolCatalog.ts} +3 -12
  323. package/src/tools/{toolRegistry.ts → catalog/toolRegistry.ts} +7 -16
  324. package/src/tools/definitions/toolDefs.ts +1264 -0
  325. package/src/tools/{auditSession.ts → diagnostics/auditSession.ts} +2 -2
  326. package/src/tools/{auditTask.ts → diagnostics/auditTask.ts} +136 -39
  327. package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +37 -9
  328. package/src/tools/diagnostics/index.ts +6 -0
  329. package/src/tools/{safeStatus.ts → diagnostics/safeStatus.ts} +7 -7
  330. package/src/tools/{safeViews.ts → diagnostics/safeViews.ts} +27 -17
  331. package/src/tools/{schemaDriftCheck.ts → diagnostics/schemaDriftCheck.ts} +3 -3
  332. package/src/tools/{createDirectSession.ts → direct/createDirectSession.ts} +6 -6
  333. package/src/tools/{finalizeDirectSession.ts → direct/finalizeDirectSession.ts} +7 -5
  334. package/src/tools/direct/index.ts +3 -0
  335. package/src/tools/{runDirectVerificationBundle.ts → direct/runDirectVerificationBundle.ts} +2 -2
  336. package/src/tools/{discoverTools.ts → discovery/discoverTools.ts} +6 -6
  337. package/src/tools/{explainTool.ts → discovery/explainTool.ts} +3 -3
  338. package/src/tools/discovery/index.ts +3 -0
  339. package/src/tools/{invokeDiscoveredTool.ts → discovery/invokeDiscoveredTool.ts} +7 -7
  340. package/src/tools/dispatch/coreDispatch.ts +36 -31
  341. package/src/tools/dispatch/diagnosticDispatch.ts +26 -21
  342. package/src/tools/dispatch/directDispatch.ts +11 -10
  343. package/src/tools/dispatch/goalDispatch.ts +5 -4
  344. package/src/tools/dispatch/releaseDispatch.ts +3 -2
  345. package/src/tools/dispatch/validation.ts +78 -0
  346. package/src/tools/{getPlan.ts → goals/getPlan.ts} +4 -4
  347. package/src/tools/{goalSubgoalTask.ts → goals/goalSubgoalTask.ts} +41 -26
  348. package/src/tools/goals/index.ts +3 -0
  349. package/src/tools/{savePlan.ts → goals/savePlan.ts} +38 -8
  350. package/src/tools/registry.ts +17 -1261
  351. package/src/tools/{checkReleaseGate.ts → release/checkReleaseGate.ts} +3 -3
  352. package/src/tools/release/index.ts +2 -0
  353. package/src/tools/{releaseMode.ts → release/releaseMode.ts} +42 -24
  354. package/src/tools/tasks/cancelTask.ts +110 -0
  355. package/src/tools/{createTask.ts → tasks/createTask.ts} +161 -33
  356. package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +12 -16
  357. package/src/tools/{evidencePack.ts → tasks/evidencePack.ts} +36 -22
  358. package/src/tools/{getTaskFile.ts → tasks/getTaskFile.ts} +4 -4
  359. package/src/tools/{getTaskStatus.ts → tasks/getTaskStatus.ts} +13 -6
  360. package/src/tools/{getTaskStdoutTail.ts → tasks/getTaskStdoutTail.ts} +13 -11
  361. package/src/tools/{getTaskSummary.ts → tasks/getTaskSummary.ts} +46 -44
  362. package/src/tools/tasks/index.ts +18 -0
  363. package/src/tools/{listTasks.ts → tasks/listTasks.ts} +6 -5
  364. package/src/tools/{reconcileTasks.ts → tasks/reconcileTasks.ts} +228 -77
  365. package/src/tools/tasks/retryTask.ts +69 -0
  366. package/src/tools/{runTaskLoop.ts → tasks/runTaskLoop.ts} +73 -46
  367. package/src/tools/{runVerification.ts → tasks/runVerification.ts} +6 -3
  368. package/src/tools/{taskLineage.ts → tasks/taskLineage.ts} +8 -7
  369. package/src/tools/{taskOutputs.ts → tasks/taskOutputs.ts} +27 -20
  370. package/src/tools/tasks/taskStates.ts +54 -0
  371. package/src/tools/{waitForTask.ts → tasks/waitForTask.ts} +3 -12
  372. package/src/tools/{androidDoctor.ts → workspace/androidDoctor.ts} +15 -10
  373. package/src/tools/{applyPatch.ts → workspace/applyPatch.ts} +38 -16
  374. package/src/tools/{discardWorktree.ts → workspace/discardWorktree.ts} +3 -3
  375. package/src/tools/workspace/index.ts +10 -0
  376. package/src/tools/workspace/listAgents.ts +75 -0
  377. package/src/tools/{listWorkspace.ts → workspace/listWorkspace.ts} +3 -3
  378. package/src/tools/{mergeWorktree.ts → workspace/mergeWorktree.ts} +3 -3
  379. package/src/tools/{readWorkspaceFile.ts → workspace/readWorkspaceFile.ts} +8 -8
  380. package/src/tools/{recommendAgentForTask.ts → workspace/recommendAgentForTask.ts} +3 -3
  381. package/src/tools/{searchWorkspace.ts → workspace/searchWorkspace.ts} +5 -5
  382. package/src/tools/workspace/syncFile.ts +292 -0
  383. package/src/utils/atomicFile.ts +74 -0
  384. package/src/utils/boundedFile.ts +116 -0
  385. package/src/utils/lockedJsonFile.ts +251 -0
  386. package/src/utils/platform.ts +31 -0
  387. package/src/utils/stableJson.ts +12 -0
  388. package/src/version.ts +3 -2
  389. package/src/watcherStatus.ts +19 -1
  390. package/ui/desktop-bootstrap.js +11 -0
  391. package/ui/desktop-bridge.js +36 -0
  392. package/ui/desktop.css +160 -0
  393. package/ui/getting-started.js +93 -0
  394. package/ui/i18n.js +484 -0
  395. package/ui/log-parser.js +30 -0
  396. package/ui/pages/audit.html +12 -0
  397. package/ui/pages/dashboard.html +94 -147
  398. package/ui/pages/direct-sessions.html +123 -26
  399. package/ui/pages/getting-started.html +43 -0
  400. package/ui/pages/logs.html +134 -22
  401. package/ui/pages/settings.html +101 -0
  402. package/ui/pages/task-detail.html +12 -0
  403. package/ui/pages/tasks.html +54 -8
  404. package/ui/pages/workspace.html +38 -1
  405. package/ui/settings.js +254 -0
  406. package/PatchWarden-Control.cmd +0 -6
  407. package/Restart-PatchWarden-Control.cmd +0 -6
  408. package/dist/test/unit/acceptance-engine.test.d.ts +0 -1
  409. package/dist/test/unit/acceptance-engine.test.js +0 -228
  410. package/dist/test/unit/agent-router.test.d.ts +0 -1
  411. package/dist/test/unit/agent-router.test.js +0 -287
  412. package/dist/test/unit/android-doctor.test.d.ts +0 -1
  413. package/dist/test/unit/android-doctor.test.js +0 -118
  414. package/dist/test/unit/apply-patch.test.d.ts +0 -1
  415. package/dist/test/unit/apply-patch.test.js +0 -225
  416. package/dist/test/unit/audit-checks.test.d.ts +0 -1
  417. package/dist/test/unit/audit-checks.test.js +0 -350
  418. package/dist/test/unit/chinese-path.test.d.ts +0 -1
  419. package/dist/test/unit/chinese-path.test.js +0 -91
  420. package/dist/test/unit/command-guard.test.d.ts +0 -1
  421. package/dist/test/unit/command-guard.test.js +0 -160
  422. package/dist/test/unit/create-task.test.d.ts +0 -1
  423. package/dist/test/unit/create-task.test.js +0 -197
  424. package/dist/test/unit/diagnose-task.test.d.ts +0 -1
  425. package/dist/test/unit/diagnose-task.test.js +0 -457
  426. package/dist/test/unit/direct-guards.test.d.ts +0 -1
  427. package/dist/test/unit/direct-guards.test.js +0 -328
  428. package/dist/test/unit/discovery-token-store.test.d.ts +0 -1
  429. package/dist/test/unit/discovery-token-store.test.js +0 -139
  430. package/dist/test/unit/evidence-pack.test.d.ts +0 -1
  431. package/dist/test/unit/evidence-pack.test.js +0 -224
  432. package/dist/test/unit/get-task-status.test.d.ts +0 -1
  433. package/dist/test/unit/get-task-status.test.js +0 -174
  434. package/dist/test/unit/get-task-summary.test.d.ts +0 -1
  435. package/dist/test/unit/get-task-summary.test.js +0 -146
  436. package/dist/test/unit/goal-graph.test.d.ts +0 -1
  437. package/dist/test/unit/goal-graph.test.js +0 -298
  438. package/dist/test/unit/goal-progress.test.d.ts +0 -1
  439. package/dist/test/unit/goal-progress.test.js +0 -381
  440. package/dist/test/unit/goal-report.test.d.ts +0 -1
  441. package/dist/test/unit/goal-report.test.js +0 -159
  442. package/dist/test/unit/goal-status.test.d.ts +0 -1
  443. package/dist/test/unit/goal-status.test.js +0 -215
  444. package/dist/test/unit/goal-store.test.d.ts +0 -1
  445. package/dist/test/unit/goal-store.test.js +0 -253
  446. package/dist/test/unit/goal-subgoal-task.test.d.ts +0 -1
  447. package/dist/test/unit/goal-subgoal-task.test.js +0 -55
  448. package/dist/test/unit/goal-tools-registry.test.d.ts +0 -1
  449. package/dist/test/unit/goal-tools-registry.test.js +0 -192
  450. package/dist/test/unit/handoff-export.test.d.ts +0 -1
  451. package/dist/test/unit/handoff-export.test.js +0 -263
  452. package/dist/test/unit/invoke-discovered-tool.test.d.ts +0 -1
  453. package/dist/test/unit/invoke-discovered-tool.test.js +0 -167
  454. package/dist/test/unit/logging.test.d.ts +0 -1
  455. package/dist/test/unit/logging.test.js +0 -397
  456. package/dist/test/unit/path-guard.test.d.ts +0 -1
  457. package/dist/test/unit/path-guard.test.js +0 -133
  458. package/dist/test/unit/post-task-cleanup.test.d.ts +0 -1
  459. package/dist/test/unit/post-task-cleanup.test.js +0 -48
  460. package/dist/test/unit/project-policy-release-mode.test.d.ts +0 -1
  461. package/dist/test/unit/project-policy-release-mode.test.js +0 -125
  462. package/dist/test/unit/reconcile-tasks.test.d.ts +0 -1
  463. package/dist/test/unit/reconcile-tasks.test.js +0 -456
  464. package/dist/test/unit/release-gate.test.d.ts +0 -1
  465. package/dist/test/unit/release-gate.test.js +0 -242
  466. package/dist/test/unit/run-task-loop.test.d.ts +0 -1
  467. package/dist/test/unit/run-task-loop.test.js +0 -380
  468. package/dist/test/unit/safe-status.test.d.ts +0 -1
  469. package/dist/test/unit/safe-status.test.js +0 -165
  470. package/dist/test/unit/safe-views.test.d.ts +0 -1
  471. package/dist/test/unit/safe-views.test.js +0 -171
  472. package/dist/test/unit/schema-drift-check.test.d.ts +0 -1
  473. package/dist/test/unit/schema-drift-check.test.js +0 -176
  474. package/dist/test/unit/sensitive-guard.test.d.ts +0 -1
  475. package/dist/test/unit/sensitive-guard.test.js +0 -104
  476. package/dist/test/unit/spec-kit-import.test.d.ts +0 -1
  477. package/dist/test/unit/spec-kit-import.test.js +0 -341
  478. package/dist/test/unit/subgoal-sync.test.d.ts +0 -1
  479. package/dist/test/unit/subgoal-sync.test.js +0 -183
  480. package/dist/test/unit/sync-file.test.d.ts +0 -1
  481. package/dist/test/unit/sync-file.test.js +0 -154
  482. package/dist/test/unit/tool-invocation-guard.test.d.ts +0 -1
  483. package/dist/test/unit/tool-invocation-guard.test.js +0 -432
  484. package/dist/test/unit/tool-usage-stats.test.d.ts +0 -1
  485. package/dist/test/unit/tool-usage-stats.test.js +0 -300
  486. package/dist/test/unit/toolSearch.test.d.ts +0 -1
  487. package/dist/test/unit/toolSearch.test.js +0 -571
  488. package/dist/test/unit/wait-for-task.test.d.ts +0 -1
  489. package/dist/test/unit/wait-for-task.test.js +0 -144
  490. package/dist/test/unit/watcher-status.test.d.ts +0 -1
  491. package/dist/test/unit/watcher-status.test.js +0 -169
  492. package/dist/test/unit/worktree-manager.test.d.ts +0 -1
  493. package/dist/test/unit/worktree-manager.test.js +0 -176
  494. package/dist/tools/applyPatch.js +0 -41
  495. package/dist/tools/cancelTask.d.ts +0 -30
  496. package/dist/tools/cancelTask.js +0 -64
  497. package/dist/tools/finalizeDirectSession.js +0 -84
  498. package/dist/tools/goalSubgoalTask.js +0 -110
  499. package/dist/tools/killTask.d.ts +0 -15
  500. package/dist/tools/listAgents.d.ts +0 -12
  501. package/dist/tools/listAgents.js +0 -42
  502. package/dist/tools/reconcileTasks.js +0 -352
  503. package/dist/tools/retryTask.js +0 -45
  504. package/dist/tools/runVerification.js +0 -36
  505. package/dist/tools/syncFile.js +0 -65
  506. package/docs/control-center/control-center-daily-driver.md +0 -211
  507. package/docs/control-center/control-center-mvp.md +0 -205
  508. package/docs/control-center/control-center-phase2.md +0 -159
  509. package/docs/execution-plan-2026-07-09.md +0 -315
  510. package/docs/release-v0.6.0.md +0 -71
  511. package/docs/release-v0.6.1.md +0 -75
  512. package/docs/release-v0.6.4.md +0 -45
  513. package/docs/roadmap-execution-and-acceptance.md +0 -365
  514. package/src/test/unit/acceptance-engine.test.ts +0 -261
  515. package/src/test/unit/agent-router.test.ts +0 -342
  516. package/src/test/unit/android-doctor.test.ts +0 -158
  517. package/src/test/unit/apply-patch.test.ts +0 -293
  518. package/src/test/unit/audit-checks.test.ts +0 -567
  519. package/src/test/unit/chinese-path.test.ts +0 -106
  520. package/src/test/unit/command-guard.test.ts +0 -221
  521. package/src/test/unit/create-task.test.ts +0 -255
  522. package/src/test/unit/diagnose-task.test.ts +0 -544
  523. package/src/test/unit/direct-guards.test.ts +0 -467
  524. package/src/test/unit/discovery-token-store.test.ts +0 -181
  525. package/src/test/unit/evidence-pack.test.ts +0 -251
  526. package/src/test/unit/get-task-status.test.ts +0 -203
  527. package/src/test/unit/get-task-summary.test.ts +0 -173
  528. package/src/test/unit/goal-graph.test.ts +0 -347
  529. package/src/test/unit/goal-progress.test.ts +0 -538
  530. package/src/test/unit/goal-report.test.ts +0 -189
  531. package/src/test/unit/goal-status.test.ts +0 -270
  532. package/src/test/unit/goal-store.test.ts +0 -318
  533. package/src/test/unit/goal-subgoal-task.test.ts +0 -72
  534. package/src/test/unit/goal-tools-registry.test.ts +0 -245
  535. package/src/test/unit/handoff-export.test.ts +0 -295
  536. package/src/test/unit/invoke-discovered-tool.test.ts +0 -216
  537. package/src/test/unit/logging.test.ts +0 -497
  538. package/src/test/unit/path-guard.test.ts +0 -180
  539. package/src/test/unit/post-task-cleanup.test.ts +0 -53
  540. package/src/test/unit/project-policy-release-mode.test.ts +0 -156
  541. package/src/test/unit/reconcile-tasks.test.ts +0 -551
  542. package/src/test/unit/release-gate.test.ts +0 -314
  543. package/src/test/unit/run-task-loop.test.ts +0 -425
  544. package/src/test/unit/safe-status.test.ts +0 -187
  545. package/src/test/unit/safe-views.test.ts +0 -184
  546. package/src/test/unit/schema-drift-check.test.ts +0 -260
  547. package/src/test/unit/sensitive-guard.test.ts +0 -124
  548. package/src/test/unit/spec-kit-import.test.ts +0 -429
  549. package/src/test/unit/subgoal-sync.test.ts +0 -236
  550. package/src/test/unit/sync-file.test.ts +0 -231
  551. package/src/test/unit/tool-invocation-guard.test.ts +0 -542
  552. package/src/test/unit/tool-usage-stats.test.ts +0 -384
  553. package/src/test/unit/toolSearch.test.ts +0 -756
  554. package/src/test/unit/wait-for-task.test.ts +0 -176
  555. package/src/test/unit/watcher-status.test.ts +0 -190
  556. package/src/test/unit/worktree-manager.test.ts +0 -247
  557. package/src/tools/cancelTask.ts +0 -70
  558. package/src/tools/listAgents.ts +0 -54
  559. package/src/tools/retryTask.ts +0 -53
  560. package/src/tools/syncFile.ts +0 -122
  561. /package/dist/tools/{toolCatalog.d.ts → catalog/toolCatalog.d.ts} +0 -0
  562. /package/dist/tools/{toolSearch.d.ts → catalog/toolSearch.d.ts} +0 -0
  563. /package/dist/tools/{toolSearch.js → catalog/toolSearch.js} +0 -0
  564. /package/dist/tools/{toolUsageStats.d.ts → catalog/toolUsageStats.d.ts} +0 -0
  565. /package/dist/tools/{toolUsageStats.js → catalog/toolUsageStats.js} +0 -0
  566. /package/dist/tools/{createDirectSession.d.ts → direct/createDirectSession.d.ts} +0 -0
  567. /package/dist/tools/{runDirectVerificationBundle.d.ts → direct/runDirectVerificationBundle.d.ts} +0 -0
  568. /package/dist/tools/{getPlan.d.ts → goals/getPlan.d.ts} +0 -0
  569. /package/dist/tools/{savePlan.d.ts → goals/savePlan.d.ts} +0 -0
  570. /package/dist/tools/{evidencePack.d.ts → tasks/evidencePack.d.ts} +0 -0
  571. /package/dist/tools/{getTaskFile.d.ts → tasks/getTaskFile.d.ts} +0 -0
  572. /package/dist/tools/{getTaskProgress.d.ts → tasks/getTaskProgress.d.ts} +0 -0
  573. /package/dist/tools/{getTaskProgress.js → tasks/getTaskProgress.js} +0 -0
  574. /package/dist/tools/{getTaskStdoutTail.d.ts → tasks/getTaskStdoutTail.d.ts} +0 -0
  575. /package/dist/tools/{getTaskSummary.d.ts → tasks/getTaskSummary.d.ts} +0 -0
  576. /package/dist/tools/{killTask.js → tasks/killTask.js} +0 -0
  577. /package/dist/tools/{retryTask.d.ts → tasks/retryTask.d.ts} +0 -0
  578. /package/dist/tools/{runVerification.d.ts → tasks/runVerification.d.ts} +0 -0
  579. /package/dist/tools/{taskLineage.d.ts → tasks/taskLineage.d.ts} +0 -0
  580. /package/dist/tools/{waitForTask.d.ts → tasks/waitForTask.d.ts} +0 -0
  581. /package/dist/tools/{androidDoctor.d.ts → workspace/androidDoctor.d.ts} +0 -0
  582. /package/dist/tools/{discardWorktree.d.ts → workspace/discardWorktree.d.ts} +0 -0
  583. /package/dist/tools/{listWorkspace.d.ts → workspace/listWorkspace.d.ts} +0 -0
  584. /package/dist/tools/{mergeWorktree.d.ts → workspace/mergeWorktree.d.ts} +0 -0
  585. /package/dist/tools/{readWorkspaceFile.d.ts → workspace/readWorkspaceFile.d.ts} +0 -0
  586. /package/dist/tools/{recommendAgentForTask.d.ts → workspace/recommendAgentForTask.d.ts} +0 -0
  587. /package/dist/tools/{searchWorkspace.d.ts → workspace/searchWorkspace.d.ts} +0 -0
  588. /package/src/tools/{toolSearch.ts → catalog/toolSearch.ts} +0 -0
  589. /package/src/tools/{toolUsageStats.ts → catalog/toolUsageStats.ts} +0 -0
  590. /package/src/tools/{getTaskProgress.ts → tasks/getTaskProgress.ts} +0 -0
  591. /package/src/tools/{killTask.ts → tasks/killTask.ts} +0 -0
package/dist/logging.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import { createHash } from "node:crypto";
2
- import { appendFileSync, mkdirSync } from "node:fs";
2
+ import { mkdirSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { redactSensitiveContent } from "./security/contentRedaction.js";
5
- import { stableJsonStringify } from "./tools/toolRegistry.js";
5
+ import { stableJsonStringify } from "./utils/stableJson.js";
6
6
  import { getConfig } from "./config.js";
7
+ import { appendBoundedTextFileSync } from "./utils/boundedFile.js";
8
+ const MAX_INVOCATION_LOG_BYTES = 5 * 1024 * 1024;
7
9
  // ── Helpers ───────────────────────────────────────────────────────
8
10
  /**
9
11
  * Returns true when PATCHWARDEN_VERBOSE_LOG is set to "true".
@@ -179,7 +181,7 @@ export function logToolInvocation(entry, options) {
179
181
  }
180
182
  mkdirSync(logsDir, { recursive: true });
181
183
  const logFilePath = join(logsDir, "invocation.log");
182
- appendFileSync(logFilePath, JSON.stringify(entry) + "\n", "utf8");
184
+ appendBoundedTextFileSync(logFilePath, JSON.stringify(entry) + "\n", MAX_INVOCATION_LOG_BYTES);
183
185
  }
184
186
  catch (err) {
185
187
  // 日志失败不应阻断主流程,仅向 stderr 记录错误
@@ -276,10 +276,13 @@ function pathMatchesPattern(relPath, pattern) {
276
276
  relPath.toLowerCase().startsWith(`${normalizedPattern.toLowerCase()}/`);
277
277
  }
278
278
  function parseGithubRepo(repository) {
279
+ const repositoryObject = repository && typeof repository === "object" && !Array.isArray(repository)
280
+ ? repository
281
+ : null;
279
282
  const raw = typeof repository === "string"
280
283
  ? repository
281
- : repository && typeof repository === "object" && typeof repository.url === "string"
282
- ? repository.url
284
+ : typeof repositoryObject?.url === "string"
285
+ ? repositoryObject.url
283
286
  : "";
284
287
  const match = raw.match(/github\.com[:/](.+?\/.+?)(?:\.git)?(?:[#?].*)?$/i);
285
288
  return match ? match[1] : null;
@@ -30,7 +30,7 @@ export interface PackedCheckResult extends StageCheckResult {
30
30
  }
31
31
  export interface HttpResponse {
32
32
  statusCode: number;
33
- data: any;
33
+ data: unknown;
34
34
  }
35
35
  export type HttpGetFn = (url: string, headers?: Record<string, string>, timeoutMs?: number) => Promise<HttpResponse>;
36
36
  export interface ReleaseGateOptions {
@@ -14,11 +14,13 @@
14
14
  * Network errors resolve to "not_checked" so transient issues don't block.
15
15
  * GITHUB_TOKEN is used as Bearer auth but never logged or returned.
16
16
  */
17
- import { execSync } from "node:child_process";
18
17
  import { createHash } from "node:crypto";
19
- import { readFileSync, writeFileSync } from "node:fs";
18
+ import { readFileSync } from "node:fs";
20
19
  import { join } from "node:path";
21
20
  import { get } from "node:https";
21
+ import { resolveTrustedCommandLine } from "../runner/processSecurity.js";
22
+ import { runSimpleProcessSync } from "../runner/simpleProcess.js";
23
+ import { atomicWriteJsonFileSync } from "../utils/atomicFile.js";
22
24
  // ── Constants ──────────────────────────────────────────────────────
23
25
  const STAGE_ORDER = [
24
26
  "local_ready",
@@ -29,6 +31,7 @@ const STAGE_ORDER = [
29
31
  ];
30
32
  const LOCAL_TIMEOUT_MS = 300000;
31
33
  const REMOTE_TIMEOUT_MS = 10000;
34
+ const MAX_REMOTE_RESPONSE_BYTES = 5 * 1024 * 1024;
32
35
  const USER_AGENT = "PatchWarden-ReleaseGate";
33
36
  const PACK_FORBIDDEN_PATTERNS = [
34
37
  /(^|[\\/])\.env$/,
@@ -55,6 +58,14 @@ export function computeTokenDigest(token) {
55
58
  export async function httpsGetJson(url, headers, timeoutMs) {
56
59
  const timeout = timeoutMs ?? REMOTE_TIMEOUT_MS;
57
60
  return new Promise((resolve, reject) => {
61
+ let settled = false;
62
+ const fail = (error) => {
63
+ if (settled)
64
+ return;
65
+ settled = true;
66
+ clearTimeout(timer);
67
+ reject(error);
68
+ };
58
69
  const timer = setTimeout(() => {
59
70
  req.destroy(new Error(`Request timed out after ${timeout}ms`));
60
71
  }, timeout);
@@ -65,9 +76,31 @@ export async function httpsGetJson(url, headers, timeoutMs) {
65
76
  ...(headers || {}),
66
77
  },
67
78
  }, (res) => {
79
+ const declaredLength = Number(res.headers["content-length"]);
80
+ if (Number.isFinite(declaredLength) && declaredLength > MAX_REMOTE_RESPONSE_BYTES) {
81
+ res.destroy();
82
+ fail(new Error(`Response exceeds ${MAX_REMOTE_RESPONSE_BYTES} bytes`));
83
+ return;
84
+ }
68
85
  const chunks = [];
69
- res.on("data", (chunk) => chunks.push(chunk));
86
+ let totalBytes = 0;
87
+ let oversized = false;
88
+ res.on("data", (chunk) => {
89
+ if (oversized)
90
+ return;
91
+ totalBytes += chunk.length;
92
+ if (totalBytes > MAX_REMOTE_RESPONSE_BYTES) {
93
+ oversized = true;
94
+ res.destroy();
95
+ fail(new Error(`Response exceeds ${MAX_REMOTE_RESPONSE_BYTES} bytes`));
96
+ return;
97
+ }
98
+ chunks.push(chunk);
99
+ });
70
100
  res.on("end", () => {
101
+ if (oversized || settled)
102
+ return;
103
+ settled = true;
71
104
  clearTimeout(timer);
72
105
  const body = Buffer.concat(chunks).toString("utf-8");
73
106
  let data = null;
@@ -81,11 +114,10 @@ export async function httpsGetJson(url, headers, timeoutMs) {
81
114
  }
82
115
  resolve({ statusCode: res.statusCode ?? 0, data });
83
116
  });
117
+ res.on("aborted", () => fail(new Error("Response was aborted before completion")));
118
+ res.on("error", fail);
84
119
  });
85
- req.on("error", (err) => {
86
- clearTimeout(timer);
87
- reject(err);
88
- });
120
+ req.on("error", fail);
89
121
  });
90
122
  }
91
123
  /**
@@ -138,12 +170,14 @@ export function checkLocalReady(repoPath) {
138
170
  const commands = ["npm.cmd run build", "npm.cmd test", "npm.cmd run doctor:ci"];
139
171
  for (const cmdStr of commands) {
140
172
  try {
141
- execSync(cmdStr, {
142
- cwd: repoPath,
143
- encoding: "utf-8",
144
- timeout: LOCAL_TIMEOUT_MS,
145
- stdio: ["ignore", "pipe", "pipe"],
146
- });
173
+ const result = runLocalCommand(repoPath, cmdStr, LOCAL_TIMEOUT_MS);
174
+ if (result.spawnError)
175
+ throw new Error(result.spawnError);
176
+ if (result.timedOut)
177
+ throw new Error(`Command timed out after ${LOCAL_TIMEOUT_MS}ms`);
178
+ if (result.exitCode !== 0) {
179
+ throw Object.assign(new Error(`Command exited with code ${result.exitCode ?? -1}`), { status: result.exitCode ?? -1 });
180
+ }
147
181
  }
148
182
  catch (err) {
149
183
  if (err && typeof err === "object" && "status" in err) {
@@ -170,12 +204,14 @@ export function checkLocalReady(repoPath) {
170
204
  export function checkPackedReady(repoPath) {
171
205
  let packOutput;
172
206
  try {
173
- packOutput = execSync("npm.cmd pack --dry-run --json", {
174
- cwd: repoPath,
175
- encoding: "utf-8",
176
- timeout: LOCAL_TIMEOUT_MS,
177
- stdio: ["ignore", "pipe", "pipe"],
178
- });
207
+ const result = runLocalCommand(repoPath, "npm.cmd pack --dry-run --json", LOCAL_TIMEOUT_MS, 10 * 1024 * 1024);
208
+ if (result.spawnError)
209
+ throw new Error(result.spawnError);
210
+ if (result.timedOut)
211
+ throw new Error(`npm pack timed out after ${LOCAL_TIMEOUT_MS}ms`);
212
+ if (result.exitCode !== 0)
213
+ throw new Error(`npm pack exited with code ${result.exitCode ?? -1}`);
214
+ packOutput = result.stdout;
179
215
  }
180
216
  catch (err) {
181
217
  return {
@@ -240,7 +276,7 @@ export function checkPackedReady(repoPath) {
240
276
  };
241
277
  const manifestPath = join(repoPath, "release-artifact-manifest.json");
242
278
  try {
243
- writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
279
+ atomicWriteJsonFileSync(manifestPath, manifest);
244
280
  }
245
281
  catch (err) {
246
282
  return {
@@ -264,6 +300,17 @@ export function checkPackedReady(repoPath) {
264
300
  }
265
301
  return { status: "passed", manifestPath };
266
302
  }
303
+ function runLocalCommand(repoPath, commandLine, timeoutMs, maxStdoutBytes = 512 * 1024) {
304
+ const invocation = resolveTrustedCommandLine(commandLine, repoPath);
305
+ return runSimpleProcessSync({
306
+ command: invocation.command,
307
+ args: invocation.argsPrefix,
308
+ cwd: repoPath,
309
+ timeoutMs,
310
+ maxStdoutBytes,
311
+ maxStderrBytes: 512 * 1024,
312
+ });
313
+ }
267
314
  /**
268
315
  * Stage 3: published_verified
269
316
  * Queries the npm registry and confirms the given version exists.
@@ -280,11 +327,12 @@ export async function checkPublishedVerified(packageName, version, httpGet) {
280
327
  if (res.statusCode === 404) {
281
328
  return { status: "failed", reason: `Package "${packageName}" not found on npm registry` };
282
329
  }
283
- const data = res.data;
284
- if (!data || !data.versions || typeof data.versions !== "object") {
330
+ const data = asRecord(res.data);
331
+ const versions = asRecord(data?.versions);
332
+ if (!versions) {
285
333
  return { status: "failed", reason: "npm registry response missing versions object" };
286
334
  }
287
- if (Object.prototype.hasOwnProperty.call(data.versions, version)) {
335
+ if (Object.prototype.hasOwnProperty.call(versions, version)) {
288
336
  return { status: "passed" };
289
337
  }
290
338
  return {
@@ -340,12 +388,12 @@ export async function checkCiVerified(repo, branch, httpGet) {
340
388
  if (res.statusCode !== 200) {
341
389
  return { status: "not_checked", reason: `GitHub API returned ${res.statusCode}` };
342
390
  }
343
- const data = res.data;
391
+ const data = asRecord(res.data);
344
392
  const runs = Array.isArray(data?.workflow_runs) ? data.workflow_runs : [];
345
393
  if (runs.length === 0) {
346
394
  return { status: "not_checked", reason: `No CI runs found for branch "${branch}"` };
347
395
  }
348
- const conclusion = runs[0]?.conclusion;
396
+ const conclusion = asRecord(runs[0])?.conclusion;
349
397
  if (conclusion === "success") {
350
398
  return { status: "passed" };
351
399
  }
@@ -364,6 +412,11 @@ export async function checkCiVerified(repo, branch, httpGet) {
364
412
  };
365
413
  }
366
414
  }
415
+ function asRecord(value) {
416
+ return value && typeof value === "object" && !Array.isArray(value)
417
+ ? value
418
+ : null;
419
+ }
367
420
  // ── Orchestrator ───────────────────────────────────────────────────
368
421
  /**
369
422
  * Run release-gate stages up to and including targetStage, in order.
@@ -6,7 +6,21 @@ export interface AgentInvocation {
6
6
  commandLabel: string;
7
7
  promptMode: "inline" | "file";
8
8
  promptFilePath?: string;
9
+ environmentVariableNames: string[];
10
+ blockedEnvironmentVariableNames: string[];
9
11
  }
12
+ export interface ResolvedAgentLaunch {
13
+ command: string;
14
+ argsPrefix: string[];
15
+ }
16
+ /**
17
+ * Resolve the Windows npm shim used by OpenCode to its native executable.
18
+ * Node cannot spawn .cmd/.ps1 shims directly with shell disabled (EINVAL), and
19
+ * enabling a shell would make the task prompt part of shell parsing.
20
+ */
21
+ export declare function resolveAgentExecutable(agentName: string, command: string, platform?: NodeJS.Platform, pathValue?: string, fileExists?: (path: string) => boolean, cwd?: string): string;
22
+ /** Resolve a configured Agent to a native executable or verified npm CLI. */
23
+ export declare function resolveAgentLaunch(agentName: string, command: string, platform?: NodeJS.Platform, pathValue?: string, fileExists?: (path: string) => boolean, cwd?: string, adapterName?: string, readText?: (path: string) => string): ResolvedAgentLaunch;
10
24
  /**
11
25
  * Build agent invocation parameters from config.
12
26
  * Replaces {repo}, {prompt}, and {prompt_file} placeholders.
@@ -1,5 +1,89 @@
1
- import { basename } from "node:path";
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { basename, win32 } from "node:path";
2
3
  import { guardAgentCommand, sanitizePromptArg } from "../security/commandGuard.js";
4
+ import { resolveTrustedExecutable, sanitizeTrustedPath } from "./processSecurity.js";
5
+ const KNOWN_AGENT_NPM_PACKAGES = Object.freeze({
6
+ codex: "@openai/codex",
7
+ claude: "@anthropic-ai/claude-code",
8
+ gemini: "@google/gemini-cli",
9
+ copilot: "@github/copilot",
10
+ qwen: "@qwen-code/qwen-code",
11
+ opencode: "opencode-ai",
12
+ });
13
+ /**
14
+ * Resolve the Windows npm shim used by OpenCode to its native executable.
15
+ * Node cannot spawn .cmd/.ps1 shims directly with shell disabled (EINVAL), and
16
+ * enabling a shell would make the task prompt part of shell parsing.
17
+ */
18
+ export function resolveAgentExecutable(agentName, command, platform = process.platform, pathValue = process.env.PATH || "", fileExists = existsSync, cwd = process.cwd()) {
19
+ return resolveAgentLaunch(agentName, command, platform, pathValue, fileExists, cwd).command;
20
+ }
21
+ /** Resolve a configured Agent to a native executable or verified npm CLI. */
22
+ export function resolveAgentLaunch(agentName, command, platform = process.platform, pathValue = process.env.PATH || "", fileExists = existsSync, cwd = process.cwd(), adapterName = agentName, readText = (path) => readFileSync(path, "utf-8")) {
23
+ if (platform !== "win32")
24
+ return { command, argsPrefix: [] };
25
+ const commandName = win32.basename(command).toLowerCase();
26
+ if (adapterName === "opencode" && new Set(["opencode", "opencode.cmd", "opencode.ps1", "opencode.bat"]).has(commandName)) {
27
+ const roots = win32.isAbsolute(command)
28
+ ? [win32.dirname(command)]
29
+ : sanitizeTrustedPath(pathValue, cwd, "win32").split(win32.delimiter).filter(Boolean);
30
+ for (const root of roots) {
31
+ const nativeExecutable = win32.join(root, "node_modules", "opencode-ai", "bin", "opencode.exe");
32
+ if (fileExists(nativeExecutable))
33
+ return { command: nativeExecutable, argsPrefix: [] };
34
+ }
35
+ }
36
+ const resolved = resolveTrustedExecutable(command, cwd, {
37
+ platform,
38
+ pathValue,
39
+ fileExists,
40
+ });
41
+ if (!/\.(?:cmd|bat|ps1)$/i.test(resolved))
42
+ return { command: resolved, argsPrefix: [] };
43
+ const packageName = KNOWN_AGENT_NPM_PACKAGES[adapterName] || KNOWN_AGENT_NPM_PACKAGES[agentName];
44
+ if (!packageName) {
45
+ throw new Error(`Windows shell shim is not allowed for Agent "${agentName}": ${resolved}`);
46
+ }
47
+ const packageRoot = win32.resolve(win32.dirname(resolved), "node_modules", ...packageName.split("/"));
48
+ const manifestPath = win32.join(packageRoot, "package.json");
49
+ if (!fileExists(manifestPath)) {
50
+ throw new Error(`Verified npm package manifest not found for Agent "${agentName}": ${manifestPath}`);
51
+ }
52
+ let manifest;
53
+ try {
54
+ manifest = JSON.parse(readText(manifestPath));
55
+ }
56
+ catch {
57
+ throw new Error(`Invalid npm package manifest for Agent "${agentName}": ${manifestPath}`);
58
+ }
59
+ if (manifest.name !== packageName) {
60
+ throw new Error(`Unexpected npm package identity for Agent "${agentName}"`);
61
+ }
62
+ const shimName = win32.basename(resolved).replace(/\.(?:cmd|bat|ps1)$/i, "");
63
+ const binPath = resolvePackageBin(manifest.bin, [agentName, adapterName, shimName]);
64
+ if (!binPath)
65
+ throw new Error(`npm package does not declare a CLI for Agent "${agentName}"`);
66
+ const cliPath = win32.resolve(packageRoot, binPath);
67
+ const relativeCli = win32.relative(packageRoot, cliPath);
68
+ if (!relativeCli || relativeCli === ".." || relativeCli.startsWith(`..${win32.sep}`) || win32.isAbsolute(relativeCli) || !fileExists(cliPath)) {
69
+ throw new Error(`npm CLI entry escapes or is missing for Agent "${agentName}"`);
70
+ }
71
+ const node = resolveTrustedExecutable("node", cwd, { platform, pathValue, fileExists });
72
+ return { command: node, argsPrefix: [cliPath] };
73
+ }
74
+ function resolvePackageBin(bin, names) {
75
+ if (typeof bin === "string")
76
+ return bin;
77
+ if (!bin || typeof bin !== "object" || Array.isArray(bin))
78
+ return null;
79
+ const entries = bin;
80
+ for (const name of names) {
81
+ if (typeof entries[name] === "string")
82
+ return entries[name];
83
+ }
84
+ const values = Object.values(entries).filter((value) => typeof value === "string");
85
+ return values.length === 1 ? values[0] : null;
86
+ }
3
87
  /**
4
88
  * Build agent invocation parameters from config.
5
89
  * Replaces {repo}, {prompt}, and {prompt_file} placeholders.
@@ -7,6 +91,12 @@ import { guardAgentCommand, sanitizePromptArg } from "../security/commandGuard.j
7
91
  */
8
92
  export function buildAgentInvocation(agentName, repoPath, prompt, config, promptFilePath) {
9
93
  const agentCmd = guardAgentCommand(agentName, config);
94
+ const launch = resolveAgentLaunch(agentName, agentCmd.command, process.platform, process.env.PATH || "", existsSync, repoPath, config.agents[agentName]?.adapter || agentName);
95
+ const environmentVariableNames = [...(config.agents[agentName]?.envAllowlist ?? [])];
96
+ const blockedEnvironmentVariableNames = [
97
+ "CONTROL_PLANE_API_KEY",
98
+ config.http?.ownerTokenEnv || "PATCHWARDEN_OWNER_TOKEN",
99
+ ];
10
100
  const sanitizedPrompt = sanitizePromptArg(prompt);
11
101
  const hasPromptFilePlaceholder = agentCmd.args.includes("{prompt_file}");
12
102
  const promptMode = hasPromptFilePlaceholder && promptFilePath ? "file" : "inline";
@@ -20,11 +110,13 @@ export function buildAgentInvocation(agentName, repoPath, prompt, config, prompt
20
110
  return arg;
21
111
  });
22
112
  return {
23
- command: agentCmd.command,
24
- args: resolvedArgs,
113
+ command: launch.command,
114
+ args: [...launch.argsPrefix, ...resolvedArgs],
25
115
  cwd: repoPath,
26
- commandLabel: `${basename(agentCmd.command)} (configured agent command)`,
116
+ commandLabel: `${basename(launch.command)} (configured agent command)`,
27
117
  promptMode,
118
+ environmentVariableNames,
119
+ blockedEnvironmentVariableNames,
28
120
  ...(promptMode === "file" && promptFilePath ? { promptFilePath } : {}),
29
121
  };
30
122
  }
@@ -51,6 +51,8 @@ export interface ChangeArtifacts {
51
51
  diff: string;
52
52
  diff_available: boolean;
53
53
  diff_truncated: boolean;
54
+ diff_redacted?: boolean;
55
+ diff_redaction_categories?: string[];
54
56
  diff_size_bytes: number;
55
57
  additions: number;
56
58
  deletions: number;
@@ -66,10 +68,16 @@ export interface ChangeArtifacts {
66
68
  unavailable_reason: string | null;
67
69
  artifact_hygiene: ArtifactHygiene;
68
70
  }
69
- export declare function captureRepoSnapshot(repoPath: string): Promise<RepoSnapshot>;
71
+ export declare function captureRepoSnapshot(repoPath: string, signal?: AbortSignal): Promise<RepoSnapshot>;
70
72
  export declare function writeSnapshot(taskDir: string, filename: string, snapshot: RepoSnapshot): void;
71
- export declare function buildChangeArtifacts(repoPath: string, before: RepoSnapshot, after: RepoSnapshot): Promise<ChangeArtifacts>;
72
- export declare function compareSnapshots(before: RepoSnapshot, after: RepoSnapshot): ChangedFile[];
73
+ export declare function buildChangeArtifacts(repoPath: string, before: RepoSnapshot, after: RepoSnapshot, signal?: AbortSignal): Promise<ChangeArtifacts>;
74
+ export declare function sanitizeDiffEvidence(input: string, maxBytes?: number): {
75
+ content: string;
76
+ truncated: boolean;
77
+ redacted: boolean;
78
+ redaction_categories: string[];
79
+ };
80
+ export declare function compareSnapshots(before: RepoSnapshot, after: RepoSnapshot, platform?: NodeJS.Platform): ChangedFile[];
73
81
  export declare function emptyArtifactHygiene(): ArtifactHygiene;
74
82
  export interface ExternalDirtyFile {
75
83
  path: string;
@@ -81,13 +89,13 @@ export interface ExternalDirtyFile {
81
89
  * Extract files that are dirty in the workspace but outside the target repo.
82
90
  * Used to establish a baseline before task execution.
83
91
  */
84
- export declare function extractExternalDirtyFiles(workspaceSnapshot: RepoSnapshot, repoPath: string, workspaceRoot: string): ExternalDirtyFile[];
92
+ export declare function extractExternalDirtyFiles(workspaceSnapshot: RepoSnapshot, repoPath: string, workspaceRoot: string, platform?: NodeJS.Platform): ExternalDirtyFile[];
85
93
  /**
86
94
  * Compare external dirty files between baseline and post-task snapshots.
87
95
  * Returns files that are NEW (not present in baseline) or CHANGED
88
96
  * (same path but different sha256, meaning the task modified them).
89
97
  */
90
- export declare function findNewExternalDirtyFiles(baseline: ExternalDirtyFile[], current: ExternalDirtyFile[]): ExternalDirtyFile[];
98
+ export declare function findNewExternalDirtyFiles(baseline: ExternalDirtyFile[], current: ExternalDirtyFile[], platform?: NodeJS.Platform): ExternalDirtyFile[];
91
99
  export interface ArtifactManifestEntry {
92
100
  path: string;
93
101
  type: string;