patchwarden 1.5.0 → 1.6.1

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 (597) hide show
  1. package/PatchWarden.cmd +32 -2
  2. package/README.en.md +194 -26
  3. package/README.md +160 -22
  4. package/dist/assessments/agentAssessor.d.ts +1 -1
  5. package/dist/assessments/agentAssessor.js +24 -15
  6. package/dist/assessments/assessmentStore.d.ts +1 -1
  7. package/dist/assessments/assessmentStore.js +44 -36
  8. package/dist/assessments/confirmCli.js +5 -4
  9. package/dist/config.d.ts +22 -0
  10. package/dist/config.js +98 -11
  11. package/dist/control/fileManager.d.ts +2 -0
  12. package/dist/control/fileManager.js +39 -0
  13. package/dist/control/middleware/auth.d.ts +16 -0
  14. package/dist/control/middleware/auth.js +17 -0
  15. package/dist/control/middleware/static.d.ts +3 -0
  16. package/dist/control/middleware/static.js +78 -0
  17. package/dist/control/routeTable.d.ts +24 -0
  18. package/dist/control/routeTable.js +263 -0
  19. package/dist/control/routes/audit.d.ts +15 -0
  20. package/dist/control/routes/audit.js +287 -0
  21. package/dist/control/routes/evidence.d.ts +4 -0
  22. package/dist/control/routes/evidence.js +96 -0
  23. package/dist/control/routes/lineage.d.ts +3 -0
  24. package/dist/control/routes/lineage.js +71 -0
  25. package/dist/control/routes/policy.d.ts +3 -0
  26. package/dist/control/routes/policy.js +81 -0
  27. package/dist/control/routes/process.d.ts +12 -0
  28. package/dist/control/routes/process.js +487 -0
  29. package/dist/control/routes/sessions.d.ts +30 -0
  30. package/dist/control/routes/sessions.js +287 -0
  31. package/dist/control/routes/status.d.ts +51 -0
  32. package/dist/control/routes/status.js +307 -0
  33. package/dist/control/routes/taskActions.d.ts +21 -0
  34. package/dist/control/routes/taskActions.js +197 -0
  35. package/dist/control/routes/tasks.d.ts +24 -0
  36. package/dist/control/routes/tasks.js +309 -0
  37. package/dist/control/routes/workspace.d.ts +15 -0
  38. package/dist/control/routes/workspace.js +220 -0
  39. package/dist/control/runtime.d.ts +92 -0
  40. package/dist/control/runtime.js +425 -0
  41. package/dist/control/server.d.ts +13 -0
  42. package/dist/control/server.js +150 -0
  43. package/dist/control/shared.d.ts +59 -0
  44. package/dist/control/shared.js +341 -0
  45. package/dist/controlCenter.d.ts +6 -0
  46. package/dist/controlCenter.js +7 -2197
  47. package/dist/direct/directAudit.js +234 -225
  48. package/dist/direct/directGuards.d.ts +2 -1
  49. package/dist/direct/directGuards.js +72 -37
  50. package/dist/direct/directPatch.d.ts +6 -1
  51. package/dist/direct/directPatch.js +55 -8
  52. package/dist/direct/directSessionStore.d.ts +16 -6
  53. package/dist/direct/directSessionStore.js +127 -35
  54. package/dist/direct/directVerification.js +8 -16
  55. package/dist/doctor.d.ts +18 -1
  56. package/dist/doctor.js +614 -373
  57. package/dist/goal/acceptanceEngine.d.ts +1 -1
  58. package/dist/goal/goalProgress.js +75 -69
  59. package/dist/goal/goalReport.d.ts +54 -0
  60. package/dist/goal/goalReport.js +197 -0
  61. package/dist/goal/goalStatus.d.ts +8 -0
  62. package/dist/goal/goalStatus.js +10 -3
  63. package/dist/goal/goalStore.d.ts +9 -2
  64. package/dist/goal/goalStore.js +152 -45
  65. package/dist/goal/handoffExport.js +6 -6
  66. package/dist/goal/specKitImport.d.ts +63 -0
  67. package/dist/goal/specKitImport.js +221 -0
  68. package/dist/goal/subgoalSync.js +13 -10
  69. package/dist/goal/worktreeManager.d.ts +2 -0
  70. package/dist/goal/worktreeManager.js +209 -136
  71. package/dist/httpServer.js +126 -44
  72. package/dist/index.js +7 -4
  73. package/dist/logging.d.ts +7 -1
  74. package/dist/logging.js +13 -3
  75. package/dist/policy/projectPolicy.js +5 -2
  76. package/dist/release/releaseGate.d.ts +1 -1
  77. package/dist/release/releaseGate.js +78 -25
  78. package/dist/runner/agentInvocation.d.ts +14 -0
  79. package/dist/runner/agentInvocation.js +96 -4
  80. package/dist/runner/changeCapture.d.ts +14 -6
  81. package/dist/runner/changeCapture.js +287 -81
  82. package/dist/runner/cli.js +7 -6
  83. package/dist/runner/postTaskCleanup.js +60 -7
  84. package/dist/runner/processSecurity.d.ts +56 -0
  85. package/dist/runner/processSecurity.js +373 -0
  86. package/dist/runner/runTask.d.ts +1 -1
  87. package/dist/runner/runTask.js +339 -287
  88. package/dist/runner/simpleProcess.d.ts +3 -0
  89. package/dist/runner/simpleProcess.js +52 -38
  90. package/dist/{taskProgress.d.ts → runner/taskProgress.d.ts} +1 -1
  91. package/dist/{taskProgress.js → runner/taskProgress.js} +2 -2
  92. package/dist/{taskRuntime.d.ts → runner/taskRuntime.d.ts} +1 -1
  93. package/dist/{taskRuntime.js → runner/taskRuntime.js} +3 -2
  94. package/dist/runner/taskStatusStore.d.ts +11 -0
  95. package/dist/runner/taskStatusStore.js +24 -0
  96. package/dist/runner/watch.d.ts +14 -1
  97. package/dist/runner/watch.js +265 -54
  98. package/dist/security/contentRedaction.d.ts +6 -0
  99. package/dist/security/contentRedaction.js +24 -2
  100. package/dist/security/discoveryTokenStore.d.ts +3 -2
  101. package/dist/security/discoveryTokenStore.js +17 -2
  102. package/dist/security/loopbackHost.d.ts +2 -0
  103. package/dist/security/loopbackHost.js +16 -0
  104. package/dist/security/pathGuard.js +32 -17
  105. package/dist/security/planGuard.js +2 -1
  106. package/dist/security/runtimeGuard.js +9 -6
  107. package/dist/security/secretComparison.d.ts +3 -0
  108. package/dist/security/secretComparison.js +11 -0
  109. package/dist/security/sensitiveGuard.d.ts +5 -0
  110. package/dist/security/sensitiveGuard.js +27 -8
  111. package/dist/security/toolInvocationGuard.d.ts +2 -2
  112. package/dist/security/toolInvocationGuard.js +1 -1
  113. package/dist/security/workspaceRootGuard.d.ts +8 -0
  114. package/dist/security/workspaceRootGuard.js +47 -0
  115. package/dist/smoke-test.js +280 -273
  116. package/dist/tools/catalog/index.d.ts +4 -0
  117. package/dist/tools/catalog/index.js +4 -0
  118. package/dist/tools/{toolCatalog.js → catalog/toolCatalog.js} +3 -12
  119. package/dist/tools/{toolRegistry.d.ts → catalog/toolRegistry.d.ts} +3 -3
  120. package/dist/tools/{toolRegistry.js → catalog/toolRegistry.js} +28 -15
  121. package/dist/tools/definitions/toolDefs.d.ts +18 -0
  122. package/dist/tools/definitions/toolDefs.js +1177 -0
  123. package/dist/tools/{auditSession.d.ts → diagnostics/auditSession.d.ts} +1 -1
  124. package/dist/tools/{auditSession.js → diagnostics/auditSession.js} +2 -2
  125. package/dist/tools/{auditTask.d.ts → diagnostics/auditTask.d.ts} +8 -63
  126. package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +111 -37
  127. package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +6 -6
  128. package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +10 -10
  129. package/dist/tools/diagnostics/index.d.ts +6 -0
  130. package/dist/tools/diagnostics/index.js +6 -0
  131. package/dist/tools/{safeStatus.d.ts → diagnostics/safeStatus.d.ts} +3 -3
  132. package/dist/tools/{safeStatus.js → diagnostics/safeStatus.js} +6 -6
  133. package/dist/tools/{safeViews.d.ts → diagnostics/safeViews.d.ts} +14 -10
  134. package/dist/tools/{safeViews.js → diagnostics/safeViews.js} +25 -15
  135. package/dist/tools/{schemaDriftCheck.d.ts → diagnostics/schemaDriftCheck.d.ts} +1 -1
  136. package/dist/tools/{schemaDriftCheck.js → diagnostics/schemaDriftCheck.js} +2 -2
  137. package/dist/tools/{createDirectSession.d.ts → direct/createDirectSession.d.ts} +1 -1
  138. package/dist/tools/{createDirectSession.js → direct/createDirectSession.js} +8 -8
  139. package/dist/tools/{finalizeDirectSession.d.ts → direct/finalizeDirectSession.d.ts} +2 -2
  140. package/dist/tools/direct/finalizeDirectSession.js +86 -0
  141. package/dist/tools/direct/index.d.ts +3 -0
  142. package/dist/tools/direct/index.js +3 -0
  143. package/dist/tools/{runDirectVerificationBundle.js → direct/runDirectVerificationBundle.js} +2 -2
  144. package/dist/tools/{discoverTools.d.ts → discovery/discoverTools.d.ts} +2 -2
  145. package/dist/tools/{discoverTools.js → discovery/discoverTools.js} +5 -5
  146. package/dist/tools/{explainTool.d.ts → discovery/explainTool.d.ts} +2 -2
  147. package/dist/tools/{explainTool.js → discovery/explainTool.js} +2 -2
  148. package/dist/tools/discovery/index.d.ts +3 -0
  149. package/dist/tools/discovery/index.js +3 -0
  150. package/dist/tools/{invokeDiscoveredTool.d.ts → discovery/invokeDiscoveredTool.d.ts} +2 -2
  151. package/dist/tools/{invokeDiscoveredTool.js → discovery/invokeDiscoveredTool.js} +5 -5
  152. package/dist/tools/dispatch/coreDispatch.d.ts +9 -0
  153. package/dist/tools/dispatch/coreDispatch.js +283 -0
  154. package/dist/tools/dispatch/diagnosticDispatch.d.ts +12 -0
  155. package/dist/tools/dispatch/diagnosticDispatch.js +80 -0
  156. package/dist/tools/dispatch/directDispatch.d.ts +8 -0
  157. package/dist/tools/dispatch/directDispatch.js +116 -0
  158. package/dist/tools/dispatch/goalDispatch.d.ts +8 -0
  159. package/dist/tools/dispatch/goalDispatch.js +92 -0
  160. package/dist/tools/dispatch/releaseDispatch.d.ts +8 -0
  161. package/dist/tools/dispatch/releaseDispatch.js +46 -0
  162. package/dist/tools/dispatch/types.d.ts +23 -0
  163. package/dist/tools/dispatch/types.js +15 -0
  164. package/dist/tools/dispatch/validation.d.ts +7 -0
  165. package/dist/tools/dispatch/validation.js +71 -0
  166. package/dist/tools/{getPlan.js → goals/getPlan.js} +4 -4
  167. package/dist/tools/{goalSubgoalTask.d.ts → goals/goalSubgoalTask.d.ts} +2 -2
  168. package/dist/tools/goals/goalSubgoalTask.js +118 -0
  169. package/dist/tools/goals/index.d.ts +3 -0
  170. package/dist/tools/goals/index.js +3 -0
  171. package/dist/tools/{savePlan.js → goals/savePlan.js} +28 -8
  172. package/dist/tools/registry.d.ts +10 -14
  173. package/dist/tools/registry.js +47 -1651
  174. package/dist/tools/{checkReleaseGate.d.ts → release/checkReleaseGate.d.ts} +1 -1
  175. package/dist/tools/{checkReleaseGate.js → release/checkReleaseGate.js} +3 -3
  176. package/dist/tools/release/index.d.ts +2 -0
  177. package/dist/tools/release/index.js +2 -0
  178. package/dist/tools/{releaseMode.d.ts → release/releaseMode.d.ts} +2 -2
  179. package/dist/tools/{releaseMode.js → release/releaseMode.js} +46 -26
  180. package/dist/tools/tasks/cancelTask.d.ts +11 -0
  181. package/dist/tools/tasks/cancelTask.js +85 -0
  182. package/dist/tools/{createTask.d.ts → tasks/createTask.d.ts} +12 -4
  183. package/dist/tools/{createTask.js → tasks/createTask.js} +46 -31
  184. package/dist/tools/{diagnoseTask.d.ts → tasks/diagnoseTask.d.ts} +1 -1
  185. package/dist/tools/{diagnoseTask.js → tasks/diagnoseTask.js} +8 -8
  186. package/dist/tools/{evidencePack.d.ts → tasks/evidencePack.d.ts} +6 -0
  187. package/dist/tools/tasks/evidencePack.js +387 -0
  188. package/dist/tools/{getTaskFile.js → tasks/getTaskFile.js} +4 -4
  189. package/dist/tools/{getTaskStatus.d.ts → tasks/getTaskStatus.d.ts} +6 -1
  190. package/dist/tools/{getTaskStatus.js → tasks/getTaskStatus.js} +5 -5
  191. package/dist/tools/{getTaskStdoutTail.js → tasks/getTaskStdoutTail.js} +13 -11
  192. package/dist/tools/{getTaskSummary.js → tasks/getTaskSummary.js} +33 -26
  193. package/dist/tools/tasks/index.d.ts +18 -0
  194. package/dist/tools/tasks/index.js +18 -0
  195. package/dist/tools/tasks/killTask.d.ts +1 -0
  196. package/dist/tools/{listTasks.d.ts → tasks/listTasks.d.ts} +1 -1
  197. package/dist/tools/{listTasks.js → tasks/listTasks.js} +3 -3
  198. package/dist/tools/{reconcileTasks.d.ts → tasks/reconcileTasks.d.ts} +1 -1
  199. package/dist/tools/{reconcileTasks.js → tasks/reconcileTasks.js} +64 -66
  200. package/dist/tools/{retryTask.d.ts → tasks/retryTask.d.ts} +2 -2
  201. package/dist/tools/tasks/retryTask.js +60 -0
  202. package/dist/tools/{runTaskLoop.d.ts → tasks/runTaskLoop.d.ts} +6 -6
  203. package/dist/tools/{runTaskLoop.js → tasks/runTaskLoop.js} +59 -31
  204. package/dist/tools/tasks/runVerification.js +38 -0
  205. package/dist/tools/{taskLineage.js → tasks/taskLineage.js} +8 -7
  206. package/dist/tools/{taskOutputs.js → tasks/taskOutputs.js} +17 -16
  207. package/dist/tools/{androidDoctor.js → workspace/androidDoctor.js} +16 -10
  208. package/dist/tools/{applyPatch.d.ts → workspace/applyPatch.d.ts} +1 -1
  209. package/dist/tools/workspace/applyPatch.js +53 -0
  210. package/dist/tools/{discardWorktree.js → workspace/discardWorktree.js} +3 -3
  211. package/dist/tools/workspace/index.d.ts +10 -0
  212. package/dist/tools/workspace/index.js +10 -0
  213. package/dist/tools/{listAgents.d.ts → workspace/listAgents.d.ts} +5 -0
  214. package/dist/tools/{listAgents.js → workspace/listAgents.js} +8 -4
  215. package/dist/tools/{listWorkspace.js → workspace/listWorkspace.js} +3 -3
  216. package/dist/tools/{mergeWorktree.js → workspace/mergeWorktree.js} +3 -3
  217. package/dist/tools/{readWorkspaceFile.js → workspace/readWorkspaceFile.js} +8 -8
  218. package/dist/tools/{recommendAgentForTask.js → workspace/recommendAgentForTask.js} +3 -3
  219. package/dist/tools/{searchWorkspace.js → workspace/searchWorkspace.js} +5 -5
  220. package/dist/tools/{syncFile.d.ts → workspace/syncFile.d.ts} +1 -1
  221. package/dist/tools/workspace/syncFile.js +134 -0
  222. package/dist/utils/atomicFile.d.ts +7 -0
  223. package/dist/utils/atomicFile.js +48 -0
  224. package/dist/utils/boundedFile.d.ts +8 -0
  225. package/dist/utils/boundedFile.js +93 -0
  226. package/dist/utils/lockedJsonFile.d.ts +15 -0
  227. package/dist/utils/lockedJsonFile.js +224 -0
  228. package/dist/utils/platform.d.ts +11 -0
  229. package/dist/utils/platform.js +26 -0
  230. package/dist/utils/stableJson.d.ts +2 -0
  231. package/dist/utils/stableJson.js +12 -0
  232. package/dist/version.d.ts +3 -2
  233. package/dist/version.js +3 -2
  234. package/dist/watcherStatus.d.ts +2 -2
  235. package/dist/watcherStatus.js +18 -0
  236. package/docs/CODE_WIKI.md +1648 -0
  237. package/docs/agentseal-integration.md +150 -0
  238. package/docs/architecture.md +23 -0
  239. package/docs/assets/patchwarden-desktop-onboarding.png +0 -0
  240. package/docs/assets/patchwarden-oss-demo.gif +0 -0
  241. package/docs/control-center/README.md +4 -4
  242. package/docs/dashboard-overview.md +86 -0
  243. package/docs/demo.md +8 -0
  244. package/docs/desktop-app.md +175 -0
  245. package/docs/direct-session-workflow.md +98 -0
  246. package/docs/evidence-pack-schema.md +215 -0
  247. package/docs/lineage-evidence-pack-workflow.md +127 -0
  248. package/docs/mcp-inspector-testing.md +200 -0
  249. package/docs/open-source-application.md +168 -0
  250. package/docs/opencode-worker.md +151 -0
  251. package/docs/openhands-worker.md +181 -0
  252. package/docs/release-checklist.md +1 -1
  253. package/docs/release-evidence.md +82 -0
  254. package/docs/spec-kit-integration.md +131 -0
  255. package/docs/task-safe-review-workflow.md +98 -0
  256. package/docs/threat-model.md +97 -0
  257. package/docs/user-feedback.md +40 -0
  258. package/docs/why-patchwarden.md +110 -0
  259. package/examples/config.example.json +4 -2
  260. package/package.json +28 -11
  261. package/scripts/README.md +15 -6
  262. package/scripts/build.js +11 -0
  263. package/scripts/checks/brand-check.js +3 -0
  264. package/scripts/checks/build-output-check.js +37 -0
  265. package/scripts/checks/control-center-smoke.js +447 -6
  266. package/scripts/checks/control-smoke.js +19 -8
  267. package/scripts/checks/http-mcp-smoke.js +82 -4
  268. package/scripts/checks/lifecycle-smoke.js +39 -33
  269. package/scripts/checks/mcp-manifest-check.js +1 -1
  270. package/scripts/checks/mcp-smoke.js +30 -4
  271. package/scripts/checks/package-manifest-check.js +20 -7
  272. package/scripts/checks/unit-tests.js +3 -5
  273. package/scripts/checks/watcher-supervisor-smoke.js +9 -0
  274. package/scripts/control/get-patchwarden-health.ps1 +1 -1
  275. package/scripts/control/manage-patchwarden.ps1 +69 -4
  276. package/scripts/control/provision-patchwarden-tunnel.ps1 +127 -0
  277. package/scripts/control/run-background-supervisor.ps1 +41 -0
  278. package/scripts/control/start-patchwarden-tunnel.ps1 +126 -32
  279. package/scripts/generate-demo-gif.py +320 -0
  280. package/{PatchWarden-Control-Tray.cmd → scripts/launchers/PatchWarden-Control-Tray.cmd} +3 -3
  281. package/scripts/launchers/PatchWarden-Control.cmd +6 -0
  282. package/{PatchWarden-Desktop.cmd → scripts/launchers/PatchWarden-Desktop.cmd} +2 -2
  283. package/scripts/launchers/Restart-PatchWarden-Control.cmd +6 -0
  284. package/{Stop-PatchWarden.cmd → scripts/launchers/Stop-PatchWarden.cmd} +2 -2
  285. package/scripts/lib/clean-generated-output.js +11 -0
  286. package/scripts/lib/file-discovery.js +12 -0
  287. package/scripts/release/desktop-preflight.js +211 -0
  288. package/scripts/release/pack-clean.js +59 -14
  289. package/src/assessments/agentAssessor.ts +29 -15
  290. package/src/assessments/assessmentStore.ts +55 -43
  291. package/src/assessments/confirmCli.ts +5 -4
  292. package/src/config.ts +105 -11
  293. package/src/control/fileManager.ts +40 -0
  294. package/src/control/middleware/auth.ts +35 -0
  295. package/src/control/middleware/static.ts +84 -0
  296. package/src/control/routeTable.ts +301 -0
  297. package/src/control/routes/audit.ts +329 -0
  298. package/src/control/routes/evidence.ts +107 -0
  299. package/src/control/routes/lineage.ts +92 -0
  300. package/src/control/routes/policy.ts +81 -0
  301. package/src/control/routes/process.ts +505 -0
  302. package/src/control/routes/sessions.ts +314 -0
  303. package/src/control/routes/status.ts +405 -0
  304. package/src/control/routes/taskActions.ts +214 -0
  305. package/src/control/routes/tasks.ts +331 -0
  306. package/src/control/routes/workspace.ts +234 -0
  307. package/src/control/runtime.ts +508 -0
  308. package/src/control/server.ts +165 -0
  309. package/src/control/shared.ts +360 -0
  310. package/src/controlCenter.ts +7 -2347
  311. package/src/direct/directAudit.ts +17 -9
  312. package/src/direct/directGuards.ts +100 -51
  313. package/src/direct/directPatch.ts +95 -8
  314. package/src/direct/directSessionStore.ts +211 -55
  315. package/src/direct/directVerification.ts +9 -23
  316. package/src/doctor.ts +766 -492
  317. package/src/goal/acceptanceEngine.ts +1 -1
  318. package/src/goal/goalProgress.ts +17 -13
  319. package/src/goal/goalReport.ts +263 -0
  320. package/src/goal/goalStatus.ts +25 -3
  321. package/src/goal/goalStore.ts +241 -54
  322. package/src/goal/handoffExport.ts +6 -6
  323. package/src/goal/specKitImport.ts +355 -0
  324. package/src/goal/subgoalSync.ts +15 -10
  325. package/src/goal/worktreeManager.ts +139 -22
  326. package/src/httpServer.ts +130 -44
  327. package/src/index.ts +7 -4
  328. package/src/logging.ts +20 -4
  329. package/src/policy/projectPolicy.ts +5 -2
  330. package/src/release/releaseGate.ts +74 -27
  331. package/src/runner/agentInvocation.ts +126 -4
  332. package/src/runner/changeCapture.ts +342 -80
  333. package/src/runner/cli.ts +7 -6
  334. package/src/runner/postTaskCleanup.ts +55 -7
  335. package/src/runner/processSecurity.ts +433 -0
  336. package/src/runner/runTask.ts +441 -292
  337. package/src/runner/simpleProcess.ts +68 -33
  338. package/src/{taskProgress.ts → runner/taskProgress.ts} +3 -3
  339. package/src/{taskRuntime.ts → runner/taskRuntime.ts} +4 -3
  340. package/src/runner/taskStatusStore.ts +54 -0
  341. package/src/runner/watch.ts +290 -57
  342. package/src/security/contentRedaction.ts +31 -2
  343. package/src/security/discoveryTokenStore.ts +18 -4
  344. package/src/security/loopbackHost.ts +19 -0
  345. package/src/security/pathGuard.ts +35 -16
  346. package/src/security/planGuard.ts +2 -1
  347. package/src/security/runtimeGuard.ts +9 -6
  348. package/src/security/secretComparison.ts +13 -0
  349. package/src/security/sensitiveGuard.ts +27 -8
  350. package/src/security/toolInvocationGuard.ts +2 -2
  351. package/src/security/workspaceRootGuard.ts +53 -0
  352. package/src/smoke-test.ts +276 -273
  353. package/src/tools/catalog/index.ts +4 -0
  354. package/src/tools/{toolCatalog.ts → catalog/toolCatalog.ts} +3 -12
  355. package/src/tools/{toolRegistry.ts → catalog/toolRegistry.ts} +29 -16
  356. package/src/tools/definitions/toolDefs.ts +1264 -0
  357. package/src/tools/{auditSession.ts → diagnostics/auditSession.ts} +2 -2
  358. package/src/tools/{auditTask.ts → diagnostics/auditTask.ts} +196 -89
  359. package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +11 -11
  360. package/src/tools/diagnostics/index.ts +6 -0
  361. package/src/tools/{safeStatus.ts → diagnostics/safeStatus.ts} +7 -7
  362. package/src/tools/{safeViews.ts → diagnostics/safeViews.ts} +29 -19
  363. package/src/tools/{schemaDriftCheck.ts → diagnostics/schemaDriftCheck.ts} +3 -3
  364. package/src/tools/{createDirectSession.ts → direct/createDirectSession.ts} +9 -9
  365. package/src/tools/{finalizeDirectSession.ts → direct/finalizeDirectSession.ts} +11 -9
  366. package/src/tools/direct/index.ts +3 -0
  367. package/src/tools/{runDirectVerificationBundle.ts → direct/runDirectVerificationBundle.ts} +2 -2
  368. package/src/tools/{discoverTools.ts → discovery/discoverTools.ts} +6 -6
  369. package/src/tools/{explainTool.ts → discovery/explainTool.ts} +3 -3
  370. package/src/tools/discovery/index.ts +3 -0
  371. package/src/tools/{invokeDiscoveredTool.ts → discovery/invokeDiscoveredTool.ts} +7 -7
  372. package/src/tools/dispatch/coreDispatch.ts +379 -0
  373. package/src/tools/dispatch/diagnosticDispatch.ts +106 -0
  374. package/src/tools/dispatch/directDispatch.ts +168 -0
  375. package/src/tools/dispatch/goalDispatch.ts +128 -0
  376. package/src/tools/dispatch/releaseDispatch.ts +66 -0
  377. package/src/tools/dispatch/types.ts +24 -0
  378. package/src/tools/dispatch/validation.ts +78 -0
  379. package/src/tools/{getPlan.ts → goals/getPlan.ts} +4 -4
  380. package/src/tools/{goalSubgoalTask.ts → goals/goalSubgoalTask.ts} +43 -28
  381. package/src/tools/goals/index.ts +3 -0
  382. package/src/tools/{savePlan.ts → goals/savePlan.ts} +38 -8
  383. package/src/tools/registry.ts +54 -1858
  384. package/src/tools/{checkReleaseGate.ts → release/checkReleaseGate.ts} +3 -3
  385. package/src/tools/release/index.ts +2 -0
  386. package/src/tools/{releaseMode.ts → release/releaseMode.ts} +42 -24
  387. package/src/tools/tasks/cancelTask.ts +109 -0
  388. package/src/tools/{createTask.ts → tasks/createTask.ts} +56 -33
  389. package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +8 -8
  390. package/src/tools/tasks/evidencePack.ts +504 -0
  391. package/src/tools/{getTaskFile.ts → tasks/getTaskFile.ts} +4 -4
  392. package/src/tools/{getTaskStatus.ts → tasks/getTaskStatus.ts} +7 -5
  393. package/src/tools/{getTaskStdoutTail.ts → tasks/getTaskStdoutTail.ts} +13 -11
  394. package/src/tools/{getTaskSummary.ts → tasks/getTaskSummary.ts} +44 -33
  395. package/src/tools/tasks/index.ts +18 -0
  396. package/src/tools/{listTasks.ts → tasks/listTasks.ts} +4 -4
  397. package/src/tools/{reconcileTasks.ts → tasks/reconcileTasks.ts} +68 -66
  398. package/src/tools/tasks/retryTask.ts +69 -0
  399. package/src/tools/{runTaskLoop.ts → tasks/runTaskLoop.ts} +70 -35
  400. package/src/tools/{runVerification.ts → tasks/runVerification.ts} +6 -3
  401. package/src/tools/{taskLineage.ts → tasks/taskLineage.ts} +8 -7
  402. package/src/tools/{taskOutputs.ts → tasks/taskOutputs.ts} +27 -20
  403. package/src/tools/{androidDoctor.ts → workspace/androidDoctor.ts} +15 -10
  404. package/src/tools/{applyPatch.ts → workspace/applyPatch.ts} +38 -16
  405. package/src/tools/{discardWorktree.ts → workspace/discardWorktree.ts} +3 -3
  406. package/src/tools/workspace/index.ts +10 -0
  407. package/src/tools/{listAgents.ts → workspace/listAgents.ts} +11 -4
  408. package/src/tools/{listWorkspace.ts → workspace/listWorkspace.ts} +3 -3
  409. package/src/tools/{mergeWorktree.ts → workspace/mergeWorktree.ts} +3 -3
  410. package/src/tools/{readWorkspaceFile.ts → workspace/readWorkspaceFile.ts} +8 -8
  411. package/src/tools/{recommendAgentForTask.ts → workspace/recommendAgentForTask.ts} +3 -3
  412. package/src/tools/{searchWorkspace.ts → workspace/searchWorkspace.ts} +5 -5
  413. package/src/tools/workspace/syncFile.ts +292 -0
  414. package/src/utils/atomicFile.ts +58 -0
  415. package/src/utils/boundedFile.ts +116 -0
  416. package/src/utils/lockedJsonFile.ts +251 -0
  417. package/src/utils/platform.ts +31 -0
  418. package/src/utils/stableJson.ts +12 -0
  419. package/src/version.ts +3 -2
  420. package/src/watcherStatus.ts +19 -1
  421. package/ui/desktop-bootstrap.js +11 -0
  422. package/ui/desktop-bridge.js +36 -0
  423. package/ui/desktop.css +160 -0
  424. package/ui/getting-started.js +93 -0
  425. package/ui/i18n.js +484 -0
  426. package/ui/log-parser.js +30 -0
  427. package/ui/pages/audit.html +204 -3
  428. package/ui/pages/dashboard.html +937 -79
  429. package/ui/pages/direct-sessions.html +609 -60
  430. package/ui/pages/getting-started.html +43 -0
  431. package/ui/pages/logs.html +105 -22
  432. package/ui/pages/settings.html +101 -0
  433. package/ui/pages/task-detail.html +468 -438
  434. package/ui/pages/tasks.html +648 -65
  435. package/ui/pages/workspace.html +38 -1
  436. package/ui/settings.js +245 -0
  437. package/PatchWarden-Control.cmd +0 -6
  438. package/Restart-PatchWarden-Control.cmd +0 -6
  439. package/dist/test/unit/acceptance-engine.test.d.ts +0 -1
  440. package/dist/test/unit/acceptance-engine.test.js +0 -228
  441. package/dist/test/unit/agent-router.test.d.ts +0 -1
  442. package/dist/test/unit/agent-router.test.js +0 -287
  443. package/dist/test/unit/android-doctor.test.d.ts +0 -1
  444. package/dist/test/unit/android-doctor.test.js +0 -118
  445. package/dist/test/unit/audit-checks.test.d.ts +0 -1
  446. package/dist/test/unit/audit-checks.test.js +0 -350
  447. package/dist/test/unit/chinese-path.test.d.ts +0 -1
  448. package/dist/test/unit/chinese-path.test.js +0 -91
  449. package/dist/test/unit/command-guard.test.d.ts +0 -1
  450. package/dist/test/unit/command-guard.test.js +0 -160
  451. package/dist/test/unit/diagnose-task.test.d.ts +0 -1
  452. package/dist/test/unit/diagnose-task.test.js +0 -457
  453. package/dist/test/unit/direct-guards.test.d.ts +0 -1
  454. package/dist/test/unit/direct-guards.test.js +0 -213
  455. package/dist/test/unit/discovery-token-store.test.d.ts +0 -1
  456. package/dist/test/unit/discovery-token-store.test.js +0 -139
  457. package/dist/test/unit/evidence-pack.test.d.ts +0 -1
  458. package/dist/test/unit/evidence-pack.test.js +0 -130
  459. package/dist/test/unit/goal-graph.test.d.ts +0 -1
  460. package/dist/test/unit/goal-graph.test.js +0 -298
  461. package/dist/test/unit/goal-progress.test.d.ts +0 -1
  462. package/dist/test/unit/goal-progress.test.js +0 -381
  463. package/dist/test/unit/goal-status.test.d.ts +0 -1
  464. package/dist/test/unit/goal-status.test.js +0 -215
  465. package/dist/test/unit/goal-store.test.d.ts +0 -1
  466. package/dist/test/unit/goal-store.test.js +0 -253
  467. package/dist/test/unit/goal-subgoal-task.test.d.ts +0 -1
  468. package/dist/test/unit/goal-subgoal-task.test.js +0 -55
  469. package/dist/test/unit/goal-tools-registry.test.d.ts +0 -1
  470. package/dist/test/unit/goal-tools-registry.test.js +0 -190
  471. package/dist/test/unit/handoff-export.test.d.ts +0 -1
  472. package/dist/test/unit/handoff-export.test.js +0 -263
  473. package/dist/test/unit/invoke-discovered-tool.test.d.ts +0 -1
  474. package/dist/test/unit/invoke-discovered-tool.test.js +0 -167
  475. package/dist/test/unit/logging.test.d.ts +0 -1
  476. package/dist/test/unit/logging.test.js +0 -397
  477. package/dist/test/unit/path-guard.test.d.ts +0 -1
  478. package/dist/test/unit/path-guard.test.js +0 -109
  479. package/dist/test/unit/post-task-cleanup.test.d.ts +0 -1
  480. package/dist/test/unit/post-task-cleanup.test.js +0 -48
  481. package/dist/test/unit/project-policy-release-mode.test.d.ts +0 -1
  482. package/dist/test/unit/project-policy-release-mode.test.js +0 -125
  483. package/dist/test/unit/reconcile-tasks.test.d.ts +0 -1
  484. package/dist/test/unit/reconcile-tasks.test.js +0 -456
  485. package/dist/test/unit/release-gate.test.d.ts +0 -1
  486. package/dist/test/unit/release-gate.test.js +0 -242
  487. package/dist/test/unit/run-task-loop.test.d.ts +0 -1
  488. package/dist/test/unit/run-task-loop.test.js +0 -380
  489. package/dist/test/unit/safe-status.test.d.ts +0 -1
  490. package/dist/test/unit/safe-status.test.js +0 -165
  491. package/dist/test/unit/safe-views.test.d.ts +0 -1
  492. package/dist/test/unit/safe-views.test.js +0 -171
  493. package/dist/test/unit/schema-drift-check.test.d.ts +0 -1
  494. package/dist/test/unit/schema-drift-check.test.js +0 -176
  495. package/dist/test/unit/sensitive-guard.test.d.ts +0 -1
  496. package/dist/test/unit/sensitive-guard.test.js +0 -104
  497. package/dist/test/unit/subgoal-sync.test.d.ts +0 -1
  498. package/dist/test/unit/subgoal-sync.test.js +0 -183
  499. package/dist/test/unit/sync-file.test.d.ts +0 -1
  500. package/dist/test/unit/sync-file.test.js +0 -154
  501. package/dist/test/unit/tool-invocation-guard.test.d.ts +0 -1
  502. package/dist/test/unit/tool-invocation-guard.test.js +0 -432
  503. package/dist/test/unit/tool-usage-stats.test.d.ts +0 -1
  504. package/dist/test/unit/tool-usage-stats.test.js +0 -300
  505. package/dist/test/unit/toolSearch.test.d.ts +0 -1
  506. package/dist/test/unit/toolSearch.test.js +0 -571
  507. package/dist/test/unit/watcher-status.test.d.ts +0 -1
  508. package/dist/test/unit/watcher-status.test.js +0 -169
  509. package/dist/test/unit/worktree-manager.test.d.ts +0 -1
  510. package/dist/test/unit/worktree-manager.test.js +0 -176
  511. package/dist/tools/applyPatch.js +0 -41
  512. package/dist/tools/cancelTask.d.ts +0 -30
  513. package/dist/tools/cancelTask.js +0 -64
  514. package/dist/tools/evidencePack.js +0 -168
  515. package/dist/tools/finalizeDirectSession.js +0 -84
  516. package/dist/tools/goalSubgoalTask.js +0 -110
  517. package/dist/tools/killTask.d.ts +0 -15
  518. package/dist/tools/retryTask.js +0 -45
  519. package/dist/tools/runVerification.js +0 -36
  520. package/dist/tools/syncFile.js +0 -65
  521. package/docs/control-center/control-center-daily-driver.md +0 -211
  522. package/docs/control-center/control-center-mvp.md +0 -205
  523. package/docs/control-center/control-center-phase2.md +0 -159
  524. package/docs/release-v0.6.0.md +0 -71
  525. package/docs/release-v0.6.1.md +0 -75
  526. package/docs/release-v0.6.4.md +0 -45
  527. package/src/test/unit/acceptance-engine.test.ts +0 -261
  528. package/src/test/unit/agent-router.test.ts +0 -342
  529. package/src/test/unit/android-doctor.test.ts +0 -158
  530. package/src/test/unit/audit-checks.test.ts +0 -567
  531. package/src/test/unit/chinese-path.test.ts +0 -106
  532. package/src/test/unit/command-guard.test.ts +0 -221
  533. package/src/test/unit/diagnose-task.test.ts +0 -544
  534. package/src/test/unit/direct-guards.test.ts +0 -297
  535. package/src/test/unit/discovery-token-store.test.ts +0 -181
  536. package/src/test/unit/evidence-pack.test.ts +0 -142
  537. package/src/test/unit/goal-graph.test.ts +0 -347
  538. package/src/test/unit/goal-progress.test.ts +0 -538
  539. package/src/test/unit/goal-status.test.ts +0 -270
  540. package/src/test/unit/goal-store.test.ts +0 -318
  541. package/src/test/unit/goal-subgoal-task.test.ts +0 -72
  542. package/src/test/unit/goal-tools-registry.test.ts +0 -243
  543. package/src/test/unit/handoff-export.test.ts +0 -295
  544. package/src/test/unit/invoke-discovered-tool.test.ts +0 -216
  545. package/src/test/unit/logging.test.ts +0 -497
  546. package/src/test/unit/path-guard.test.ts +0 -150
  547. package/src/test/unit/post-task-cleanup.test.ts +0 -53
  548. package/src/test/unit/project-policy-release-mode.test.ts +0 -156
  549. package/src/test/unit/reconcile-tasks.test.ts +0 -551
  550. package/src/test/unit/release-gate.test.ts +0 -314
  551. package/src/test/unit/run-task-loop.test.ts +0 -425
  552. package/src/test/unit/safe-status.test.ts +0 -187
  553. package/src/test/unit/safe-views.test.ts +0 -184
  554. package/src/test/unit/schema-drift-check.test.ts +0 -260
  555. package/src/test/unit/sensitive-guard.test.ts +0 -124
  556. package/src/test/unit/subgoal-sync.test.ts +0 -236
  557. package/src/test/unit/sync-file.test.ts +0 -231
  558. package/src/test/unit/tool-invocation-guard.test.ts +0 -542
  559. package/src/test/unit/tool-usage-stats.test.ts +0 -384
  560. package/src/test/unit/toolSearch.test.ts +0 -756
  561. package/src/test/unit/watcher-status.test.ts +0 -190
  562. package/src/test/unit/worktree-manager.test.ts +0 -247
  563. package/src/tools/cancelTask.ts +0 -70
  564. package/src/tools/evidencePack.ts +0 -205
  565. package/src/tools/retryTask.ts +0 -53
  566. package/src/tools/syncFile.ts +0 -122
  567. /package/dist/tools/{toolCatalog.d.ts → catalog/toolCatalog.d.ts} +0 -0
  568. /package/dist/tools/{toolSearch.d.ts → catalog/toolSearch.d.ts} +0 -0
  569. /package/dist/tools/{toolSearch.js → catalog/toolSearch.js} +0 -0
  570. /package/dist/tools/{toolUsageStats.d.ts → catalog/toolUsageStats.d.ts} +0 -0
  571. /package/dist/tools/{toolUsageStats.js → catalog/toolUsageStats.js} +0 -0
  572. /package/dist/tools/{runDirectVerificationBundle.d.ts → direct/runDirectVerificationBundle.d.ts} +0 -0
  573. /package/dist/tools/{getPlan.d.ts → goals/getPlan.d.ts} +0 -0
  574. /package/dist/tools/{savePlan.d.ts → goals/savePlan.d.ts} +0 -0
  575. /package/dist/tools/{getTaskFile.d.ts → tasks/getTaskFile.d.ts} +0 -0
  576. /package/dist/tools/{getTaskProgress.d.ts → tasks/getTaskProgress.d.ts} +0 -0
  577. /package/dist/tools/{getTaskProgress.js → tasks/getTaskProgress.js} +0 -0
  578. /package/dist/tools/{getTaskStdoutTail.d.ts → tasks/getTaskStdoutTail.d.ts} +0 -0
  579. /package/dist/tools/{getTaskSummary.d.ts → tasks/getTaskSummary.d.ts} +0 -0
  580. /package/dist/tools/{killTask.js → tasks/killTask.js} +0 -0
  581. /package/dist/tools/{runVerification.d.ts → tasks/runVerification.d.ts} +0 -0
  582. /package/dist/tools/{taskLineage.d.ts → tasks/taskLineage.d.ts} +0 -0
  583. /package/dist/tools/{taskOutputs.d.ts → tasks/taskOutputs.d.ts} +0 -0
  584. /package/dist/tools/{waitForTask.d.ts → tasks/waitForTask.d.ts} +0 -0
  585. /package/dist/tools/{waitForTask.js → tasks/waitForTask.js} +0 -0
  586. /package/dist/tools/{androidDoctor.d.ts → workspace/androidDoctor.d.ts} +0 -0
  587. /package/dist/tools/{discardWorktree.d.ts → workspace/discardWorktree.d.ts} +0 -0
  588. /package/dist/tools/{listWorkspace.d.ts → workspace/listWorkspace.d.ts} +0 -0
  589. /package/dist/tools/{mergeWorktree.d.ts → workspace/mergeWorktree.d.ts} +0 -0
  590. /package/dist/tools/{readWorkspaceFile.d.ts → workspace/readWorkspaceFile.d.ts} +0 -0
  591. /package/dist/tools/{recommendAgentForTask.d.ts → workspace/recommendAgentForTask.d.ts} +0 -0
  592. /package/dist/tools/{searchWorkspace.d.ts → workspace/searchWorkspace.d.ts} +0 -0
  593. /package/src/tools/{toolSearch.ts → catalog/toolSearch.ts} +0 -0
  594. /package/src/tools/{toolUsageStats.ts → catalog/toolUsageStats.ts} +0 -0
  595. /package/src/tools/{getTaskProgress.ts → tasks/getTaskProgress.ts} +0 -0
  596. /package/src/tools/{killTask.ts → tasks/killTask.ts} +0 -0
  597. /package/src/tools/{waitForTask.ts → tasks/waitForTask.ts} +0 -0
@@ -0,0 +1,1648 @@
1
+ # PatchWarden Code Wiki
2
+
3
+ > 本文档是对 PatchWarden 仓库的结构化代码导览,覆盖项目整体架构、主要模块职责、关键类与函数说明、依赖关系、运行方式,以及现有缺陷分析。
4
+ > 源码版本:**v1.6.1** · Schema Epoch:`2026-07-19-v15` · 复核日期:2026-07-20 · License:MIT
5
+
6
+ ## 目录
7
+
8
+ - [1. 项目概览](#1-项目概览)
9
+ - [2. 整体架构](#2-整体架构)
10
+ - [3. 目录结构](#3-目录结构)
11
+ - [4. 核心模块职责](#4-核心模块职责)
12
+ - [5. 关键类与函数说明](#5-关键类与函数说明)
13
+ - [6. 数据流与状态机](#6-数据流与状态机)
14
+ - [7. 依赖关系](#7-依赖关系)
15
+ - [8. 项目运行方式](#8-项目运行方式)
16
+ - [9. 安全设计](#9-安全设计)
17
+ - [10. 测试与发布](#10-测试与发布)
18
+ - [11. 现有缺陷与改进建议](#11-现有缺陷与改进建议)
19
+
20
+ ---
21
+
22
+ ## 1. 项目概览
23
+
24
+ PatchWarden 是一个面向本地编程 Agent 的**安全 MCP(Model Context Protocol)桥接器**。上游的 ChatGPT、Codex、OpenCode 或其他 MCP 客户端负责规划与验收,PatchWarden 负责把计划保存成工作区内任务,再由预先配置的本地 Agent 执行,并返回结果、代码差异和独立测试记录。
25
+
26
+ **核心定位**:
27
+
28
+ > PatchWarden is a local-first MCP safety and verification layer for AI coding agents, with workspace confinement, command allowlists, scope-violation detection, and auditable task evidence.
29
+
30
+ **关键特性**:
31
+
32
+ - MCP 工具不提供通用 Shell,上游模型只能调用明确的 MCP 工具
33
+ - 每个任务必须指定位于 `workspaceRoot` 内的 `repo_path`
34
+ - Agent 启动命令来自本地配置,不来自模型输入
35
+ - 测试命令必须精确匹配 `allowedTestCommands` 白名单
36
+ - 任务完成后保存结构化结果、有界且脱敏的差异、文件统计和独立验证记录
37
+ - 工作区外出现变化时标记为作用域违规,而非悄悄接受
38
+ - `.env`、Token、SSH 密钥、Cookie、凭据文件等敏感路径默认不可读
39
+ - v1.6.0 引入 Electron 桌面应用,支持 8 种本地 Agent 自动检测与模型发现
40
+ - v1.6.1 完成跨进程 mutation 锁串行化、TypeScript 边界收紧与 Windows 并发覆盖扩展
41
+
42
+ **技术栈**:TypeScript + Node.js(≥18);主仓只有一个直接运行时依赖 `@modelcontextprotocol/sdk`(`^1.29.0`),`desktop/` 子包的直接运行时依赖为 `jsonc-parser`、`smol-toml`、`yaml` 三个解析器。
43
+
44
+ ---
45
+
46
+ ## 2. 整体架构
47
+
48
+ ### 2.1 角色与数据流
49
+
50
+ ```text
51
+ ChatGPT / Codex / OpenCode / 其他 MCP 客户端
52
+
53
+
54
+ PatchWarden MCP Server(stdio / HTTP)
55
+
56
+ save_plan / create_task / run_task_loop
57
+
58
+
59
+ .patchwarden/tasks/<task_id>/
60
+
61
+ Watcher 发现任务
62
+
63
+
64
+ 本地 Agent(OpenCode / Codex / Claude Code / Gemini / ...)
65
+
66
+
67
+ result.json / diff.patch / verify.json / status.json
68
+
69
+
70
+ MCP 客户端读取 safe 摘要、审计证据并人工验收
71
+
72
+
73
+ Control Center Dashboard / Desktop Tray(可选)
74
+ ```
75
+
76
+ ### 2.2 三种运行角色
77
+
78
+ 一次完整运行通常包含三个独立角色:
79
+
80
+ | 角色 | 职责 | 入口 |
81
+ | --- | --- | --- |
82
+ | **MCP Server** | 暴露受限的 planning/task/summary/audit 工具 | `dist/index.js`(stdio)或 `dist/httpServer.js`(HTTP) |
83
+ | **Watcher** | 轮询 queued 任务并启动本地 Agent | `dist/runner/watch.js` |
84
+ | **本地 Agent** | 真正修改代码,必须在配置中预先登记 | 由 Watcher 通过 `spawn` 启动 |
85
+ | **Control Center**(可选) | 本地 Dashboard HTTP 服务,聚合任务/会话/lineage/证据包 | `dist/controlCenter.js`(127.0.0.1:8090) |
86
+ | **Desktop App**(可选) | Electron 外壳,引导配置、监督后端、托盘管理 | `desktop/src/main.ts` |
87
+
88
+ > ⚠️ "MCP 已连接"不等于"任务一定会执行"。如果 Watcher 没有运行,`create_task` 仍能保存任务,但任务会保持 `queued` 并返回 `execution_blocked: true`。
89
+
90
+ ### 2.3 四种工具 Profile
91
+
92
+ | Profile | 工具数 | 用途 |
93
+ | --- | --- | --- |
94
+ | `full` | 64 | 本地完整开发目录,包含核心、管理、Direct 工具 |
95
+ | `chatgpt_core` | 26 | ChatGPT Tunnel 固定的核心工具集 |
96
+ | `chatgpt_direct` | 14 | ChatGPT 直接编辑模式,需 `enableDirectProfile: true` |
97
+ | `chatgpt_search` | 5 | 动态工具发现场景(discover/explain/invoke) |
98
+
99
+ ### 2.4 两种执行模式
100
+
101
+ | 模式 | 流程 |
102
+ | --- | --- |
103
+ | **Agent 委托模式** | ChatGPT 编写计划 → 本地 Agent 执行 → PatchWarden 审计 |
104
+ | **Direct 模式** | ChatGPT 创建 session → 读取/搜索文件 → 应用 JSON 补丁 → 运行白名单验证 → finalize → audit |
105
+
106
+ ### 2.5 v1.6.0 Desktop 架构
107
+
108
+ ```text
109
+ ┌──────────────────────────────────────────────────┐
110
+ │ PatchWarden Desktop (Electron) │
111
+ │ ┌────────────────────────────────────────────┐ │
112
+ │ │ main.mjs (主进程) │ │
113
+ │ │ ├─ agent-adapters.mjs (8 种 Agent 检测) │ │
114
+ │ │ ├─ model-discovery.mjs (本地模型发现) │ │
115
+ │ │ ├─ config-store.mjs (DPAPI / 原子写入) │ │
116
+ │ │ ├─ tunnel-provisioner.mjs (隧道下发) │ │
117
+ │ │ ├─ runtime-settings.mjs (tunnel-client) │ │
118
+ │ │ ├─ backend-probe.mjs (后端探测) │ │
119
+ │ │ └─ runtime-root.mjs (主仓定位) │ │
120
+ │ └────────────────────────────────────────────┘ │
121
+ │ │ utilityProcess.fork │
122
+ │ ▼ │
123
+ │ PatchWarden Core (dist/controlCenter.js:8090) │
124
+ │ + Control Center UI (ui/pages/*) │
125
+ └──────────────────────────────────────────────────┘
126
+ ```
127
+
128
+ 桌面应用是薄壳,所有业务逻辑在主仓 `dist/`,桌面只负责安装/检测/拉起/托盘/tunnel 下发。依赖极简(3 个运行时包),采用 `contextIsolation` + `sandbox` + IPC sender 校验。
129
+
130
+ ### 2.6 v1.6.1 跨进程串行化
131
+
132
+ v1.6.1 把所有持久化状态变更收敛到统一的 `withFileLockSync` / `mutateLockedJsonFileSync` 抽象:
133
+
134
+ | 状态域 | 锁文件位置 | 串行化操作 |
135
+ | --- | --- | --- |
136
+ | Task status | `tasks/<id>/status.lock` | `pending → running` 领取、cancel、状态原子替换 |
137
+ | Watcher | `.patchwarden/watcher.lock` | 单实例排他创建 + stale PID 接管 |
138
+ | Goal status | `.patchwarden/goals/<id>/goal.lock` | mutation、子目标 accept/reject |
139
+ | Worktree | `.patchwarden/worktrees/<id>/lifecycle.lock` | create/merge/discard 仓库级锁 |
140
+ | Direct session | `direct-sessions/<id>/session.lock` | patch/sync/verify/finalize/audit |
141
+ | Assessment | `assessments/<id>/assessment.lock` | freshness 校验与读取 |
142
+ | Control events | `.patchwarden/control-center/events.lock` | append + trim + 原子替换 |
143
+ | Bounded logs | `invocation.log.lock` 等 | 跨进程有界追加 + 截断标记 |
144
+
145
+ 锁文件使用 `O_EXCL` 创建 + PID 探活,孤儿锁由后续操作自动接管;所有锁内操作完成后通过原子 `.tmp-{pid}` rename 提交,保证 crash 后磁盘上要么是旧状态、要么是新状态,不会出现半写入。
146
+
147
+ ---
148
+
149
+ ## 3. 目录结构
150
+
151
+ ```text
152
+ PatchWarden/
153
+ ├── src/ # TypeScript 源码(主仓)
154
+ │ ├── index.ts # stdio MCP Server 入口
155
+ │ ├── httpServer.ts # HTTP MCP Server 入口(127.0.0.1 only)
156
+ │ ├── controlCenter.ts # Control Center 启动入口(调用 control/server.ts)
157
+ │ ├── doctor.ts # 只读诊断脚本
158
+ │ ├── config.ts # 配置加载与校验
159
+ │ ├── errors.ts # PatchWardenError 错误模型
160
+ │ ├── logging.ts # 审计日志
161
+ │ ├── version.ts # 版本与 Schema Epoch
162
+ │ ├── watcherStatus.ts # Watcher 心跳状态
163
+ │ ├── smoke-test.ts # 烟雾测试入口
164
+ │ ├── agents/ # Agent 路由
165
+ │ │ └── agentRouter.ts
166
+ │ ├── assessments/ # 风险评估
167
+ │ │ ├── agentAssessor.ts
168
+ │ │ ├── assessmentStore.ts
169
+ │ │ └── confirmCli.ts
170
+ │ ├── control/ # Control Center 后端(v1.5.1 拆分)
171
+ │ │ ├── server.ts # HTTP 服务、路由分发、生命周期
172
+ │ │ ├── shared.ts # 共享配置、令牌、HTTP/文件 helper
173
+ │ │ ├── runtime.ts # 健康探测、stale 分类、事件时间线
174
+ │ │ ├── routeTable.ts # 声明式路由表
175
+ │ │ ├── middleware/
176
+ │ │ │ ├── auth.ts # control token 校验
177
+ │ │ │ └── static.ts # 静态文件服务
178
+ │ │ └── routes/ # 路由 handler(10 个领域)
179
+ │ │ ├── audit.ts evidence.ts lineage.ts policy.ts
180
+ │ │ ├── process.ts sessions.ts status.ts
181
+ │ │ └── taskActions.ts tasks.ts workspace.ts
182
+ │ ├── direct/ # Direct 直接编辑模式
183
+ │ │ ├── directSessionStore.ts
184
+ │ │ ├── directGuards.ts
185
+ │ │ ├── directPatch.ts
186
+ │ │ ├── directAudit.ts
187
+ │ │ └── directVerification.ts
188
+ │ ├── goal/ # Goal Session 多任务编排
189
+ │ │ ├── goalStore.ts goalGraph.ts goalStatus.ts
190
+ │ │ ├── goalProgress.ts goalReport.ts handoffExport.ts
191
+ │ │ ├── specKitImport.ts subgoalSync.ts
192
+ │ │ ├── acceptanceEngine.ts acceptanceTemplate.ts
193
+ │ │ └── worktreeManager.ts
194
+ │ ├── policy/ # 项目级策略
195
+ │ │ └── projectPolicy.ts
196
+ │ ├── release/ # 发布门控
197
+ │ │ └── releaseGate.ts
198
+ │ ├── runner/ # 任务执行核心
199
+ │ │ ├── cli.ts runTask.ts watch.ts
200
+ │ │ ├── agentInvocation.ts changeCapture.ts
201
+ │ │ ├── postTaskCleanup.ts simpleProcess.ts processSecurity.ts
202
+ │ │ └── taskRuntime.ts taskProgress.ts taskStatusStore.ts
203
+ │ ├── security/ # 纵深防御守卫
204
+ │ │ ├── commandGuard.ts pathGuard.ts sensitiveGuard.ts
205
+ │ │ ├── planGuard.ts riskEngine.ts runtimeGuard.ts
206
+ │ │ ├── contentRedaction.ts toolInvocationGuard.ts
207
+ │ │ ├── discoveryTokenStore.ts workspaceRootGuard.ts loopbackHost.ts
208
+ │ ├── tools/ # MCP 工具实现
209
+ │ │ ├── registry.ts # 工具注册中枢
210
+ │ │ ├── catalog/ # Profile / Manifest / 搜索 / 统计
211
+ │ │ │ ├── toolCatalog.ts toolRegistry.ts toolSearch.ts toolUsageStats.ts
212
+ │ │ ├── definitions/ # 工具定义
213
+ │ │ │ └── toolDefs.ts
214
+ │ │ ├── diagnostics/ # 审计与健康检查
215
+ │ │ │ ├── auditSession.ts auditTask.ts healthCheck.ts
216
+ │ │ │ ├── safeStatus.ts safeViews.ts schemaDriftCheck.ts
217
+ │ │ ├── direct/ # Direct 工具实现
218
+ │ │ ├── discovery/ # 动态工具发现
219
+ │ │ ├── dispatch/ # 工具分派层(v1.5.1 拆分)
220
+ │ │ │ ├── types.ts coreDispatch.ts diagnosticDispatch.ts
221
+ │ │ │ ├── directDispatch.ts goalDispatch.ts releaseDispatch.ts
222
+ │ │ │ └── validation.ts
223
+ │ │ ├── goals/ # Goal 工具
224
+ │ │ ├── release/ # Release mode 工具
225
+ │ │ ├── tasks/ # 任务管理工具
226
+ │ │ ├── workspace/ # 工作区与 worktree 工具
227
+ │ │ ├── registry.ts
228
+ │ │ └── taskTemplates.ts
229
+ │ └── test/unit/ # 单元测试(45+ 测试文件)
230
+ ├── desktop/ # Electron 桌面应用子包(v1.6.0)
231
+ │ ├── package.json # 独立依赖(jsonc-parser/smol-toml/yaml)
232
+ │ ├── src/ # 9 个 .ts 模块
233
+ │ │ ├── main.ts # Electron 入口、Tray、IPC、后端生命周期
234
+ │ │ ├── agent-adapters.ts # 8 种 Agent 适配器
235
+ │ │ ├── agent-detection.ts # 门面 re-export
236
+ │ │ ├── backend-probe.ts # /api/diagnostics 探测
237
+ │ │ ├── config-store.ts # 配置/DPAPI/原子写入
238
+ │ │ ├── model-discovery.ts # 各 Agent 配置文件模型抽取
239
+ │ │ ├── runtime-root.ts # 主仓定位
240
+ │ │ ├── runtime-settings.ts # tunnel-client 检测
241
+ │ │ └── tunnel-provisioner.ts # 隧道下发
242
+ │ ├── onboarding/ # 首次引导 UI
243
+ │ ├── scripts/ # stage/checksum/icon/test
244
+ │ └── test/ # 桌面单元测试
245
+ ├── ui/ # Control Center 前端
246
+ │ ├── pages/ # dashboard/tasks/audit 等 8 个页面
247
+ │ ├── partials/
248
+ │ └── vendor/ # tailwindcss / lucide
249
+ ├── scripts/ # 运维脚本
250
+ │ ├── checks/ # 烟雾测试与 manifest 校验
251
+ │ ├── control/ # PowerShell 进程管理
252
+ │ ├── launchers/ # Windows 一键启动器
253
+ │ ├── mcp/ # MCP 启动包装
254
+ │ └── release/ # 发布打包
255
+ ├── docs/ # 文档
256
+ ├── examples/ # 配置与 Tunnel 示例
257
+ ├── package.json # v1.6.1,单一直接运行时依赖
258
+ ├── tsconfig.json
259
+ └── PatchWarden.cmd # Windows 统一控制入口
260
+ ```
261
+
262
+ ---
263
+
264
+ ## 4. 核心模块职责
265
+
266
+ ### 4.1 入口层
267
+
268
+ | 文件 | 职责 |
269
+ | --- | --- |
270
+ | [src/index.ts](../src/index.ts) | stdio MCP Server 入口,加载配置、注册工具、连接 `StdioServerTransport` |
271
+ | [src/httpServer.ts](../src/httpServer.ts) | HTTP MCP Server,绑定 `127.0.0.1:7331`,每请求独立 MCP 实例,支持 owner token 与 `/admin/tasks/:id/accept` 验收端点 |
272
+ | [src/controlCenter.ts](../src/controlCenter.ts) | 本地 Dashboard 启动入口,仅 `import { startServer } from "./control/server.js"` |
273
+ | [src/doctor.ts](../src/doctor.ts) | 只读诊断脚本,检查 15+ 项:Node/npm/Git 版本、配置、工作区、路径保护、敏感文件、Agent 命令、工具 Manifest、HTTP 端口、Watcher 目录、构建产物 |
274
+
275
+ ### 4.2 配置与基础
276
+
277
+ | 文件 | 职责 |
278
+ | --- | --- |
279
+ | [src/config.ts](../src/config.ts) | 加载并校验 `patchwarden.config.json`,提供 `loadConfig`/`getConfig`/`getTasksDir`/`getPlansDir`/`resolveWorkspaceRoot`/`getRepoAllowedTestCommands`/`getRepoDirectAllowedCommands` 等路径解析;严格校验 `workspaceRoot`、`agents`、`allowedTestCommands`、`watcherStaleSeconds`、`toolProfile`、`tunnelProxy`、Direct 数值范围等字段;含 `normalizeRepoKey` 与 `comparablePath` 工具 |
280
+ | [src/errors.ts](../src/errors.ts) | 定义 `PatchWardenError`(含 `reason`/`suggestion`/`blocked`/`details`)与 `errorPayload` 序列化 |
281
+ | [src/version.ts](../src/version.ts) | 导出 `PATCHWARDEN_VERSION = "1.6.1"` 与 `TOOL_SCHEMA_EPOCH = "2026-07-19-v15"` |
282
+ | [src/logging.ts](../src/logging.ts) | `Logger` 类输出 stderr JSON 日志,记录 `audit`/`info`/`warn`/`error`;`logToolInvocation` 仅写参数 digest,不写原参数,并通过跨进程锁有界追加到 5 MiB;`installGlobalHandlers` 捕获未处理异常但不吞错 |
283
+
284
+ ### 4.3 安全模块(src/security/)
285
+
286
+ PatchWarden 的纵深防御核心,所有写操作前都会经过这些守卫。
287
+
288
+ | 文件 | 职责 |
289
+ | --- | --- |
290
+ | [commandGuard.ts](../src/security/commandGuard.ts) | 命令白名单守卫:`guardAgentCommand` 校验 agent 已配置且命令无 shell 元字符;`guardTestCommand` 精确匹配测试白名单;`guardDirectCommand` 校验 Direct 白名单;`sanitizePromptArg` 清洗控制字符 |
291
+ | [pathGuard.ts](../src/security/pathGuard.ts) | 路径横移守卫:`guardPath`/`guardReadPath`/`guardWorkspacePath` 确保路径解析后都在 `workspaceRoot` 内,用 `realpath` 防符号链接逃逸,Windows 下做盘符一致性检查 |
292
+ | [sensitiveGuard.ts](../src/security/sensitiveGuard.ts) | 敏感文件守卫:`isSensitivePath` 匹配 `.env`/SSH 私钥/`credentials`/`.npmrc`/`cookies`/`.kube/config` 等;没有 `.patchwarden/` 前缀豁免,敏感名称在任意目录深度都阻断;NTFS ADS、NUL 字节也拒绝 |
293
+ | [planGuard.ts](../src/security/planGuard.ts) | 计划内容守卫:`guardPlanContent` 扫描 plan 文本,拦截"读取密钥/破坏性删除/植入后门"等危险指令,支持中英文否定语境检测 |
294
+ | [riskEngine.ts](../src/security/riskEngine.ts) | 综合风险评估:`assessRisk` 串联各 guard 输出 `risk_level`(low/medium/high)+ `decision`(allow/needs_confirm/blocked)+ `reason_codes`;`collectRiskHints` 仅做关键词提示,不影响决策 |
295
+ | [runtimeGuard.ts](../src/security/runtimeGuard.ts) | 阻止任务把 `repo_path` 指向运行中的 PatchWarden 自身目录(`dist`/`src`/`scripts`/`release`);使用 `isSamePath`/`isPathChildOf` 做平台感知比较 |
296
+ | [contentRedaction.ts](../src/security/contentRedaction.ts) | 输出脱敏:`redactSensitiveContent`/`redactSensitiveValue` 按正则替换私钥、bearer token、npm token、GitLab `glpat-`、Slack `xox*`、AWS access key、Google API key、JWT 与凭据赋值 |
297
+ | [loopbackHost.ts](../src/security/loopbackHost.ts) | 回环 HTTP Host 白名单:仅接受当前端口的 `127.0.0.1`/`localhost`,供 HTTP MCP 与 Control Center 共同阻断 DNS rebinding |
298
+ | [toolInvocationGuard.ts](../src/security/toolInvocationGuard.ts) | `invoke_discovered_tool` 调用前 8 项校验:token 匹配、profile 允许、风险等级、敏感路径、assessment 必需、命令元字符、release 确认、credential 拒绝 |
299
+ | [discoveryTokenStore.ts](../src/security/discoveryTokenStore.ts) | 服务端 discovery token 存储:`issueToken`/`consumeToken`(单次使用)/`peekToken`/`revokeToken`,纯内存、10 分钟 TTL、活动上限 1024 |
300
+ | [workspaceRootGuard.ts](../src/security/workspaceRootGuard.ts) | `validateWorkspaceRoot` 校验工作区根目录合法性(被桌面应用动态 import 复用),拒绝盘符根、用户主目录、Desktop、Downloads、Documents |
301
+
302
+ ### 4.4 Runner 模块(src/runner/)
303
+
304
+ 任务执行的核心,编排从启动到产出 artifact 的完整生命周期。
305
+
306
+ | 文件 | 职责 |
307
+ | --- | --- |
308
+ | [cli.ts](../src/runner/cli.ts) | Runner 子进程入口,从 argv 或 `PATCHWARDEN_TASK_ID` 读取 taskId 并调用 `runTask` |
309
+ | [runTask.ts](../src/runner/runTask.ts) | **执行主循环**:`runTask(taskId)` 编排 preparing → executing_agent → running_tests → collecting_artifacts → done/failed;管理心跳(2s)、超时、cancel/kill;产出 16+ artifact 文件;区分 `failed_scope_violation`/`failed_policy_violation`;通过 `claimPendingTask` 实现单次领取 |
310
+ | [watch.ts](../src/runner/watch.ts) | 常驻 watcher,每 4 秒轮询 `pending` 任务,执行 pre-flight 安全校验后调用 `runTask`;原子写心跳文件;通过 env 注入 `WATCHER_INSTANCE_ID`/`WATCHER_LAUNCHER_PID` 支持所有权判定;单实例排他锁 + stale PID 接管 |
311
+ | [agentInvocation.ts](../src/runner/agentInvocation.ts) | 构建 agent 调用参数与 prompt:`buildAgentInvocation`/`buildExecutionPrompt`/`buildAssessmentPrompt`;占位符 `{repo}`/`{prompt}`/`{prompt_file}` 替换;Windows npm shim 解析为原生 exe 或已验证的 package `bin`,不启用 shell;Agent 必须显式注册,子进程只接收该 Agent 的 `envAllowlist` 环境变量 |
312
+ | [changeCapture.ts](../src/runner/changeCapture.ts) | 仓库快照与变更证据:`captureRepoSnapshot` 并行跑 5 个 git 命令;`buildChangeArtifacts` 比对快照生成最多 20 MiB、写前脱敏的 diff;凭据型内容记录 redaction 元数据;`extractExternalDirtyFiles` 建立外部脏文件基线;`buildArtifactManifest` 生成带 sha256 的产物清单;Windows 下使用 `nullDevice` (`NUL` / `/dev/null`) 与小写路径键 |
313
+ | [postTaskCleanup.ts](../src/runner/postTaskCleanup.ts) | 任务后清理:仅删除未被 git 跟踪且被忽略的临时产物(`__pycache__`/`dist`/`*.pyc`),三道闸门保护受控文件;使用 `fs.rmSync` 有界重试,Windows 失败时只清除只读属性后重试,不再启动 `cmd.exe /c rmdir` |
314
+ | [simpleProcess.ts](../src/runner/simpleProcess.ts) | 轻量进程执行器:`runSimpleProcess`/`runSimpleProcessSync`,使用最小子进程环境、受信 PATH 可执行文件绑定、无 shell 的 npm/npx/pnpm 解析,以及有界脱敏日志捕获;异步完成以 child `close` 为准 |
315
+ | [processSecurity.ts](../src/runner/processSecurity.ts) | `SecureProcessLogCapture` 有界收集 + 写前脱敏 + `appendFileSync`;`buildChildEnvironment` 构造最小环境;`resolveTrustedExecutable` / `resolvePackageManagerInvocation` 受信解析 |
316
+ | [taskStatusStore.ts](../src/runner/taskStatusStore.ts) | 任务状态原子存储:`claimPendingTask` 同目录硬链接锁 + 原子 JSON 替换实现单次领取;`mutateTaskStatus` 共享状态锁,与 `cancelTask` 串行化 |
317
+ | [taskRuntime.ts](../src/runner/taskRuntime.ts) | `runtime.json` 浅合并读写,含 PID 重用与孤儿任务检测字段 |
318
+ | [taskProgress.ts](../src/runner/taskProgress.ts) | `progress.md` 生成,6 阶段标记(`[x]`/`[>]`/`[ ]`) |
319
+
320
+ ### 4.5 Tools 模块(src/tools/)
321
+
322
+ MCP 工具实现与注册中枢,共 49 个工具文件。
323
+
324
+ | 文件 | 职责 |
325
+ | --- | --- |
326
+ | [registry.ts](../src/tools/registry.ts) | **工具注册中枢**(约 170 行):`registerTools` 绑定 `ListToolsRequestSchema`/`CallToolRequestSchema`,`handleToolCall` 分派到领域 handler map;工具定义由 `definitions/toolDefs.ts` 提供;启动时校验"每个已注册工具必有 handler";冻结一次连接的 active tool list 防止 list/call 漂移 |
327
+ | [catalog/toolCatalog.ts](../src/tools/catalog/toolCatalog.ts) | Profile 与 Manifest:`resolveToolProfile`/`selectToolsForProfile` 按 profile 过滤工具;`buildToolCatalogSnapshot` 计算 `tool_manifest_sha256` 用于漂移检测;导出 `CHATGPT_CORE_TOOL_NAMES`(26)/`CHATGPT_DIRECT_TOOL_NAMES`(14)/`CHATGPT_SEARCH_TOOL_NAMES`(5);`chatgpt_direct` 在未启用时降级为仅 `health_check` |
328
+ | [catalog/toolRegistry.ts](../src/tools/catalog/toolRegistry.ts) | 工具元数据:`buildToolRegistry` 为每个工具补全 risk/modes/tags/aliases/schema_digest;稳定 JSON 实现来自 `src/utils/stableJson.ts` |
329
+ | [catalog/toolSearch.ts](../src/tools/catalog/toolSearch.ts) | SafeToolSearch 搜索引擎(v0.9.0):混合排序、意图分类、风险调整与历史成功率反馈 |
330
+ | [catalog/toolUsageStats.ts](../src/tools/catalog/toolUsageStats.ts) | 从 `invocation.log` 聚合工具调用统计 |
331
+ | [diagnostics/schemaDriftCheck.ts](../src/tools/diagnostics/schemaDriftCheck.ts) | Schema 漂移自检,供 doctor 消费 |
332
+ | [tasks/createTask.ts](../src/tools/tasks/createTask.ts) | `createTask` 任务创建:支持 saved/inline/template 来源及 `assess_only` 风险预评估;定义 `TaskStatus` 与 `TaskPhase` 类型 |
333
+ | [tasks/runTaskLoop.ts](../src/tools/tasks/runTaskLoop.ts) | **安全编排入口**:组合 create_task → wait_for_task → safe summaries → audit_task,支持 worktree 隔离和 Direct 验证 |
334
+ | [tasks/taskLineage.ts](../src/tools/tasks/taskLineage.ts) | 链路记录,写入 `.patchwarden/lineages/<lineage_id>/` |
335
+ | [tasks/evidencePack.ts](../src/tools/tasks/evidencePack.ts) | 证据包导出,不含 stdout/stderr、完整 diff 或敏感内容 |
336
+ | [diagnostics/auditTask.ts](../src/tools/diagnostics/auditTask.ts) | 独立审计,区分 confirmed failures、possible false positives 与 manual verification;文档扫描最多 200 个 Markdown、4 MiB 总内容,证据读取有界且超限产生 warning |
337
+ | [diagnostics/safeViews.ts](../src/tools/diagnostics/safeViews.ts) | `safe_*` 系列有界摘要;其中 audit/finalize 包装器会写审计或会话状态,风险元数据为 `workspace_write` |
338
+ | [diagnostics/healthCheck.ts](../src/tools/diagnostics/healthCheck.ts) | 返回 MCP catalog、watcher、workspace 与 agents 状态 |
339
+ | [tasks/waitForTask.ts](../src/tools/tasks/waitForTask.ts) | 长轮询任务,返回 continuation 或终态 acceptance 证据 |
340
+ | [workspace/androidDoctor.ts](../src/tools/workspace/androidDoctor.ts) | 只读诊断 Android 构建环境 |
341
+ | [release/releaseMode.ts](../src/tools/release/releaseMode.ts) | 发布模式四件套与项目策略入口 |
342
+ | [discovery/explainTool.ts](../src/tools/discovery/explainTool.ts) | 展开单个工具详情并做 schema drift 检测 |
343
+ | [workspace/syncFile.ts](../src/tools/workspace/syncFile.ts) | Direct 会话仓库内复制文件,统一校验会话状态、真实路径、UTF-8/大小、敏感路径/内容与 sha256,并在写入前复核源/目标未变化 |
344
+ | [goals/goalSubgoalTask.ts](../src/tools/goals/goalSubgoalTask.ts) | 在 Goal mutation lock 内创建 subgoal 并关联新任务;以 Goal 保存的 `repo_path` 为权威,拒绝调用方仓库不一致,隔离 worktree 也从该仓库创建 |
345
+ | [workspace/mergeWorktree.ts](../src/tools/workspace/mergeWorktree.ts) | 合并隔离 worktree 的 branch 回主工作区 |
346
+ | [workspace/discardWorktree.ts](../src/tools/workspace/discardWorktree.ts) | 丢弃隔离 worktree 并归档状态 |
347
+ | [tasks/runVerification.ts](../src/tools/tasks/runVerification.ts) | Direct 会话跑单条白名单验证命令 |
348
+ | [direct/runDirectVerificationBundle.ts](../src/tools/direct/runDirectVerificationBundle.ts) | Direct 会话批量跑多个白名单验证命令并返回有界状态 |
349
+ | 其他工具 | `applyPatch.ts`/`searchWorkspace.ts`/`readWorkspaceFile.ts`/`savePlan.ts`/`getPlan.ts`/`listAgents.ts`/`listTasks.ts`/`listWorkspace.ts`/`cancelTask.ts`/`killTask.ts`/`retryTask.ts`/`reconcileTasks.ts`/`recommendAgentForTask.ts`/`diagnoseTask.ts`/`getTaskStatus.ts`/`getTaskSummary.ts`/`getTaskProgress.ts`/`getTaskStdoutTail.ts`/`getTaskFile.ts`/`taskOutputs.ts`/`taskTemplates.ts`/`auditSession.ts`/`createDirectSession.ts`/`finalizeDirectSession.ts`/`discoverTools.ts`/`invokeDiscoveredTool.ts`/`safeStatus.ts`/`checkReleaseGate.ts` 等 |
350
+
351
+ ### 4.6 Dispatch 子模块(src/tools/dispatch/)
352
+
353
+ v1.5.1 引入的按业务领域分组的工具分派层,将原先 `registry.ts` 中庞大的 switch 拆分为 5 个 handler map。v1.6.1 加入 `validation.ts` 提供 `unknown` 输入的类型守卫辅助。
354
+
355
+ | 文件 | 职责 |
356
+ | --- | --- |
357
+ | [types.ts](../src/tools/dispatch/types.ts) | 定义 `ToolHandler`、`ToolHandlerMap` 类型与 `toResult` 辅助函数(将任意数据 JSON 序列化为 MCP `CallToolResult` 信封) |
358
+ | [validation.ts](../src/tools/dispatch/validation.ts) | v1.6.1 新增:从 `unknown` 提取 string/number/boolean/array 的类型守卫;dispatch 层用它取代 `as any` 断言 |
359
+ | [coreDispatch.ts](../src/tools/dispatch/coreDispatch.ts) | 核心任务管理 handler(30+ 工具):`save_plan`/`get_plan`/`create_task`/`run_task_loop`/`wait_for_task`/`audit_task`/`safe_*`/`list_*`/`cancel_task`/`kill_task`/`retry_task`/`reconcile_tasks`/`check_release_gate`/`health_check` 等;导出条件注册的 `runTaskHandler` |
360
+ | [diagnosticDispatch.ts](../src/tools/dispatch/diagnosticDispatch.ts) | 诊断/发现类 handler:`discover_tools`/`explain_tool`/`invoke_discovered_tool`;后者通过 `dispatch` 回调把请求转回 `handleToolCall` 实现"发现 → 调用"闭环 |
361
+ | [directDispatch.ts](../src/tools/dispatch/directDispatch.ts) | Direct session handler(9 个):所有 handler 前置 `guardDirectProfileEnabled()` 守卫;`create_direct_session`/`search_workspace`/`apply_patch`/`run_verification`/`run_direct_verification_bundle`/`finalize_direct_session`/`audit_session`/`sync_file`/`safe_direct_*` |
362
+ | [goalDispatch.ts](../src/tools/dispatch/goalDispatch.ts) | Goal Session handler(12 个):`create_goal`/`list_goals`/`read_goal`/`create_subgoal_task`/`accept_subgoal`/`reject_subgoal`/`suggest_next_subgoal`/`summarize_goal_progress`/`export_handoff`/`export_goal_report`/`import_speckit_tasks`/`merge_worktree`/`discard_worktree` |
363
+ | [releaseDispatch.ts](../src/tools/dispatch/releaseDispatch.ts) | Release mode handler(4 个):`release_check`/`release_prepare`/`release_verify`/`release_cleanup` |
364
+
365
+ **集成方式**:`registry.ts` 的 `buildDispatchMap()` 通过对象 spread 合并 5 个 handler map,并在 `enableRunTaskTool===true` 时条件加入 `runTaskHandler`。`registerTools` 启动时校验"每个已注册工具必有 handler",但反向不校验。`handleToolCallInternal` 通过 `dispatchMap[name]` 查表分派。
366
+
367
+ ### 4.7 Goal 模块(src/goal/)
368
+
369
+ v0.8.0 引入的多任务编排层。
370
+
371
+ | 文件 | 职责 |
372
+ | --- | --- |
373
+ | [goalStore.ts](../src/goal/goalStore.ts) | Goal Session 目录 CRUD;同步/异步状态变更共享跨进程 mutation lock 并原子替换,非空 Goal 的全部子目标 accepted 后自动转为 `completed`;目录结构 `.patchwarden/goals/{goal_id}/` 含 `GOAL.md`/`GOALS.md`/`goal_status.json`/`tasks/`/`artifacts/` |
374
+ | [goalGraph.ts](../src/goal/goalGraph.ts) | 依赖图:`suggestNextSubgoal` 返回依赖已满足的下一个子目标 |
375
+ | [goalStatus.ts](../src/goal/goalStatus.ts) | `GoalStatus`/`Subgoal` 类型与 `createInitialGoalStatus` |
376
+ | [goalProgress.ts](../src/goal/goalProgress.ts) | `acceptSubgoal`/`rejectSubgoal`/`summarizeGoalProgress` |
377
+ | [goalReport.ts](../src/goal/goalReport.ts) | `exportGoalReport` 导出结构化最终报告(v1.5.1) |
378
+ | [handoffExport.ts](../src/goal/handoffExport.ts) | `exportHandoff` 导出 `handoff.md` 用于会话交接 |
379
+ | [specKitImport.ts](../src/goal/specKitImport.ts) | `parseSpecKitJson`/`importSpecKitTasks` 导入 Spec Kit 任务(v1.5.1) |
380
+ | [subgoalSync.ts](../src/goal/subgoalSync.ts) | 子目标与任务关联同步 |
381
+ | [acceptanceEngine.ts](../src/goal/acceptanceEngine.ts) | 验收引擎 |
382
+ | [acceptanceTemplate.ts](../src/goal/acceptanceTemplate.ts) | 验收模板 |
383
+ | [worktreeManager.ts](../src/goal/worktreeManager.ts) | `createWorktree`/`mergeWorktree`/`discardWorktree` 管理 git worktree 隔离;三类仓库变更共享跨进程 repository lifecycle lock |
384
+
385
+ ### 4.8 Direct 模块(src/direct/)
386
+
387
+ v0.6.0 引入的 ChatGPT 直接编辑模式。v1.6.1 把 patch/sync/verify/finalize/audit 全部纳入同一 session mutation lock 串行化。
388
+
389
+ | 文件 | 职责 |
390
+ | --- | --- |
391
+ | [directSessionStore.ts](../src/direct/directSessionStore.ts) | Direct Session CRUD:原子创建/更新记录,锁内追加 operations/verification_runs;workspace mutation 锁串行化 patch/sync/verify/finalize/audit;审计后持久化 `audited=true` |
392
+ | [directGuards.ts](../src/direct/directGuards.ts) | Direct 守卫:`guardDirectSessionActive`/`guardDirectSessionFinalized`/`guardDirectPath`/`guardDirectReadPath`/`guardDirectWritePath`/`guardDirectPatchSize`/`isBinaryFile`;拒绝 junction/symlink 与 NTFS ADS |
393
+ | [directPatch.ts](../src/direct/directPatch.ts) | JSON patch 应用:支持 `replace_exact`/`insert_before`/`insert_after`/`replace_whole_file`;同一次读取校验 `expected_sha256`、UTF-8、文件大小和敏感内容,目标同目录写随机临时文件 + `renameSync` 原子替换并保留原权限;失败时清理临时文件 |
394
+ | [directAudit.ts](../src/direct/directAudit.ts) | 17 项确定性审计,包含 diff 凭据型内容检查,返回 pass/warn/fail 并原子写入审计证据 |
395
+ | [directVerification.ts](../src/direct/directVerification.ts) | 白名单验证命令执行 |
396
+
397
+ ### 4.9 Control 模块(src/control/)
398
+
399
+ v1.5.1 将原 `controlCenter.ts` 拆分为聚焦的路由/中间件/运行时/共享模块,保持入口和 HTTP 行为不变。v1.6.1 加入 `routeTable.ts` 声明式路由表。
400
+
401
+ ```text
402
+ shared.ts (配置 bootstrap / 路径常量 / controlToken / HTTP helper)
403
+ ├─ runtime.ts (健康探测 / stale 分类 / 事件时间线 / 隐藏 ID)
404
+ ├─ routeTable.ts (声明式路由表)
405
+ ├─ middleware/
406
+ │ ├─ auth.ts (control token 校验 + 回环 Host)
407
+ │ └─ static.ts (ui/ 静态文件服务)
408
+ └─ routes/
409
+ ├─ 只读: audit / evidence / lineage / policy / sessions / status / tasks / workspace
410
+ └─ 变更: process / taskActions
411
+ ```
412
+
413
+ | 文件 | 职责 |
414
+ | --- | --- |
415
+ | [server.ts](../src/control/server.ts) | HTTP 服务创建、回环 Host/DNS rebinding 前置校验、防嵌入响应头(CSP `frame-ancestors 'none'` + `X-Frame-Options: DENY`)、`handleRequest` 路由分发、POST control-token 校验、bootstrap/shutdown 生命周期 |
416
+ | [shared.ts](../src/control/shared.ts) | 共享基础设施:容错配置加载、路径常量、内存 `controlToken = randomUUID()`、端口解析、`sendJson`/`readBody`/`readJsonFileSafe`/`readJsonFileSafeUnder`(带 realpath 校验)/`readFileTail`/`findLatestLog`/`isPathInside` |
417
+ | [runtime.ts](../src/control/runtime.ts) | `probeHealthStatus`/`probeRuntimeHealth` 健康探测;`classifyStaleTask`/`augmentTaskWithStale` 四条 stale 规则;`writeStatusFile`/`recordEvent`/`readEvents`(在跨进程文件锁内 append + trim + 原子替换);`isValidTaskId`/`isValidDirectSessionId` 校验;`readHiddenStaleIds`/`readHiddenDirectSessionIds` 隐藏 ID |
418
+ | [routeTable.ts](../src/control/routeTable.ts) | 声明式路由表:按 method + pattern(声明顺序优先)匹配,specific pattern 优先于 generic `:id` |
419
+ | [middleware/auth.ts](../src/control/middleware/auth.ts) | `checkControlToken` 校验 `x-patchwarden-control-token`,使用 `crypto.timingSafeEqual` 并先校验 Buffer 长度;复用 `security/loopbackHost.ts` 拒绝非回环 Host |
420
+ | [middleware/static.ts](../src/control/middleware/static.ts) | `serveStatic`/`serveFavicon`,三层路径遍历防护(NUL 拒绝、`..` 段拒绝、`relative()` 词法校验) |
421
+ | [routes/audit.ts](../src/control/routes/audit.ts) | `handleLogs`(core/direct/watcher/control-center 四类日志,经 `redactSensitiveContent` 脱敏);`handleAudit`(聚合 audit.json 限 50 条);`handleWarnings`(7 类警告桶) |
422
+ | [routes/evidence.ts](../src/control/routes/evidence.ts) | 证据包列表/详情/导出(POST) |
423
+ | [routes/lineage.ts](../src/control/routes/lineage.ts) | lineage 列表/详情(限 50,`toSafeTaskLineage` 投影) |
424
+ | [routes/policy.ts](../src/control/routes/policy.ts) | 项目策略 + release 就绪状态(只读,`remote_write_performed` 始终 false) |
425
+ | [routes/process.ts](../src/control/routes/process.ts) | 进程生命周期代理:start/stop/restart core/direct/all;preflight + spawn + waitForStartup;`classifySupervisorFailure` 8 类失败归类 |
426
+ | [routes/sessions.ts](../src/control/routes/sessions.ts) | Direct session 列表/详情/安全摘要/finalize/audit/hide;统一调用 `isValidDirectSessionId` 白名单校验 |
427
+ | [routes/status.ts](../src/control/routes/status.ts) | 主轮询端点 `/api/status`:10 路 Promise.all 并发探测 + 6 类建议;`/api/control-center-status`/`/api/events`/`/api/tunnel-ui-url`/`/api/diagnostics` |
428
+ | [routes/taskActions.ts](../src/control/routes/taskActions.ts) | 变更性任务操作:reconcile/audit_task/open-task-folder/hide-stale |
429
+ | [routes/tasks.ts](../src/control/routes/tasks.ts) | 只读任务查询:列表过滤/stale 任务/任务详情/safe 视图;统一调用 `reconstructTaskEntry` helper |
430
+ | [routes/workspace.ts](../src/control/routes/workspace.ts) | 工作区列表、repo 列表(含 package.json 元数据)、单 repo `git status --short`;只拒绝值恰为 `..` 的路径段 |
431
+
432
+ ### 4.10 Desktop 子包(desktop/)
433
+
434
+ v1.6.0 引入的 Electron 桌面应用,作为 Control Center 的 Windows GUI 外壳。v1.6.1 把 IPC、JSON、model-discovery、tunnel-process 数据全部从 `unknown` 校验。
435
+
436
+ | 文件 | 职责 |
437
+ | --- | --- |
438
+ | [desktop/src/main.ts](../desktop/src/main.ts) | 入口:单实例锁、BrowserWindow(1280x800,`contextIsolation`/`sandbox`/`nodeIntegration:false`)、Tray、IPC 注册、后端生命周期(`utilityProcess.fork` 拉起主仓 `dist/controlCenter.js`);模式机 `starting/setup/setup-check/ready/blocked`;`allowedSender` 限制 IPC 来源 |
439
+ | [desktop/src/agent-adapters.ts](../desktop/src/agent-adapters.ts) | `AGENT_ADAPTERS` 8 个适配器(codex/opencode/claude/gemini/copilot/qwen/kimi/aider);`detectAgents()` 用 `where.exe`/`which` 查找;`selectAgentLaunch()` 安全选择原生 exe(Win 跳 `WindowsApps`、强制 `.exe/.com`)或验证 npm 包 bin 入口;`refreshAgentModels()` 跑 `refreshArgs` 过滤模型 token;`validateModelId()` |
440
+ | [desktop/src/agent-detection.ts](../desktop/src/agent-detection.ts) | 纯 re-export 门面,把 `selectAgentLaunch` 以 `selectAgentExecutable` 别名导出 |
441
+ | [desktop/src/backend-probe.ts](../desktop/src/backend-probe.ts) | `probeControlCenter()` GET `/api/diagnostics`,返回 `kind: patchwarden/foreign/absent/mismatched_patchwarden`;`configIdentity()` = 规范化路径 sha256;`mayStopBackend()` 校验 owned child 身份一致性 |
442
+ | [desktop/src/backend-lifecycle.ts](../desktop/src/backend-lifecycle.ts) | owned backend 停止时等待 `exit` 或有界超时;配置变更触发的 restart 由 generation scheduler 串行化、合并 debounce 请求且不丢活动重启期间的新请求 |
443
+ | [desktop/src/child-environment.ts](../desktop/src/child-environment.ts) | 为 Desktop-owned utility/spawn 子进程构造最小运行环境;provider 变量必须显式 allow-list,Control/Tunnel owner credential 始终阻断;Windows PowerShell 与 `where.exe` 固定解析到仓库外的系统目录 |
444
+ | [desktop/src/config-store.ts](../desktop/src/config-store.ts) | 配置/偏好/运行时设置持久化:`resolveDesktopPaths()`(`%LOCALAPPDATA%\PatchWarden`);`buildConfig()` 生成 `patchwarden.config.json`;`normalizeProxyEndpoint()` 校验 http/https/socks5 禁止 URL 凭证;`atomicWriteJson()` 带 `.bak-{stamp}` 备份 + `.tmp-{pid}` 原子 rename |
445
+ | [desktop/src/model-discovery.ts](../desktop/src/model-discovery.ts) | `discoverModelsForAgent(id, workspaceRoot)`:读取各 Agent 的 home/工作区配置(TOML/JSONC/YAML),`safeRead()` 拒绝符号链接、>1MB、project-scoped 逃逸(`realpathSync` 比对 workspaceRoot);`MAX_CONFIG_BYTES=1MB`;按适配器抽取模型字段;只读 |
446
+ | [desktop/src/runtime-root.ts](../desktop/src/runtime-root.ts) | `resolveCoreRoot()`:打包态 → `resourcesPath/core`,开发态 → `desktopRoot/..`;`utilityProcessOptions()` 提供 cwd/env/stdio/serviceName |
447
+ | [desktop/src/runtime-settings.ts](../desktop/src/runtime-settings.ts) | `validateTunnelClientPath()` 强制绝对路径 + 文件名 `tunnel-client.exe` + 存在;`detectTunnelClient()` 多源查找(config/env/PATH/LOCALAPPDATA/APPDATA/USERPROFILE);`boundedSiblingSearch()` BFS 深度≤2、≤2000 条目 |
448
+ | [desktop/src/tunnel-provisioner.ts](../desktop/src/tunnel-provisioner.ts) | `getTunnelSetupStatus()` 报告程序/profile/凭证/tunnel_id 掩码/doctor 状态;`provisionTunnelProfile()` spawn PowerShell `scripts/control/provision-patchwarden-tunnel.ps1`,runtimeKey 走 stdin,60s 超时,输出 key 被 `[REDACTED]` 替换,env 剥离 `CONTROL_PLANE_API_KEY`;`revalidateTunnelProfile()`/`forgetTunnelCredential()`/`maskTunnelId()` |
449
+
450
+ **支持的 8 种 Agent 适配器**:
451
+
452
+ | id | displayName | npmPackage / nativePackage | buildArgs | refreshArgs |
453
+ | --- | --- | --- | --- | --- |
454
+ | `codex` | Codex CLI | `@openai/codex` | `exec --cd {repo} --model <m> {prompt}` | — |
455
+ | `opencode` | OpenCode | native `opencode-ai` | `run --model <m> {prompt}` | `models` |
456
+ | `claude` | Claude Code | `@anthropic-ai/claude-code` | `--print --permission-mode acceptEdits --model <m> {prompt}` | — |
457
+ | `gemini` | Gemini CLI | `@google/gemini-cli` | `--prompt {prompt} --approval-mode auto_edit --model <m>` | — |
458
+ | `copilot` | GitHub Copilot CLI | `@github/copilot` | `-p {prompt} --allow-tool write --deny-tool shell --model <m>` | `help` |
459
+ | `qwen` | Qwen Code | `@qwen-code/qwen-code` | `--prompt {prompt} --approval-mode auto_edit --model <m>` | — |
460
+ | `kimi` | Kimi Code | —(仅原生) | `--prompt {prompt} --work-dir {repo} --model <m>` | — |
461
+ | `aider` | Aider | —(仅原生) | `--message {prompt} --model <m>` | `--list-models ""` |
462
+
463
+ ### 4.11 其他模块
464
+
465
+ | 文件 | 职责 |
466
+ | --- | --- |
467
+ | [src/agents/agentRouter.ts](../src/agents/agentRouter.ts) | Agent 路由:`routeAgent` 按 scope 文件数与关键词推荐 agent(largeScope→opencode、singleFile→direct、audit→patchwarden-audit、refactor→codex、documentation→claude) |
468
+ | [src/policy/projectPolicy.ts](../src/policy/projectPolicy.ts) | 仓库级策略:`getProjectPolicySummary` 解析 `.patchwarden/project-policy.json`;`commandAllowedByProjectPolicy`/`isProtectedByProjectPolicy`;含 `DANGEROUS_COMMAND_RE` 防护 |
469
+ | [src/release/releaseGate.ts](../src/release/releaseGate.ts) | v1.0.0 五阶段发布门:`local_ready` → `packed_ready` → `published_verified` → `github_release_verified` → `ci_verified`;远程阶段仅用 `node:https` GET,网络错误返回 `not_checked` |
470
+ | [src/assessments/agentAssessor.ts](../src/assessments/agentAssessor.ts) | `runAgentAssessment` 调用 agent 执行只读风险评估,输出 `===ASSESSMENT_JSON===` 标记后的结构化结果 |
471
+ | [src/assessments/assessmentStore.ts](../src/assessments/assessmentStore.ts) | Assessment 记录存储与 freshness 校验;v1.6.1 加入跨进程锁串行化 freshness 校验与读取 |
472
+ | [src/utils/atomicFile.ts](../src/utils/atomicFile.ts) | `atomicWriteFileSync`/`atomicWriteJsonFileSync`:`.tmp-{pid}` 临时文件 + `renameSync` 原子替换 |
473
+ | [src/utils/boundedFile.ts](../src/utils/boundedFile.ts) | 有界读取:`readBoundedFileHead`/`readBoundedFileTail`/`readBoundedFileSlice` |
474
+ | [src/utils/lockedJsonFile.ts](../src/utils/lockedJsonFile.ts) | v1.6.1 核心:`withFileLockSync`/`mutateLockedJsonFileSync`,`O_EXCL` 创建 + PID 探活 + stale 接管 + 锁内 append/trim/原子替换 |
475
+ | [src/utils/platform.ts](../src/utils/platform.ts) | `isSamePath`/`isPathChildOf` 平台感知比较(Windows 忽略大小写) |
476
+ | [src/utils/stableJson.ts](../src/utils/stableJson.ts) | 唯一 `stableJsonStringify`;catalog 与 registry 均导入该实现 |
477
+ | [src/watcherStatus.ts](../src/watcherStatus.ts) | `readWatcherStatus` 心跳读取 + task heartbeat fallback + watcher ownership 检测;将 degraded 视为不可用 |
478
+
479
+ ---
480
+
481
+ ## 5. 关键类与函数说明
482
+
483
+ ### 5.1 核心类型
484
+
485
+ #### `PatchWardenConfig`([src/config.ts](../src/config.ts))
486
+
487
+ ```typescript
488
+ interface PatchWardenConfig {
489
+ workspaceRoot: string; // 工作区根目录(唯一允许访问)
490
+ plansDir: string; // 计划目录,相对 workspaceRoot
491
+ tasksDir: string; // 任务目录,相对 workspaceRoot
492
+ assessmentsDir: string; // 评估目录
493
+ assessmentTtlSeconds: number; // 评估 TTL(60-86400)
494
+ agents: Record<string, AgentConfig>; // Agent 白名单
495
+ allowedTestCommands: string[]; // 全局测试命令白名单(精确匹配)
496
+ repoAllowedTestCommands?: Record<string, string[]>; // 仓库专属命令
497
+ maxReadFileBytes: number; // 单次文件读取上限
498
+ defaultTaskTimeoutSeconds: number; // 默认任务超时
499
+ maxTaskTimeoutSeconds: number; // 最大任务超时
500
+ watcherStaleSeconds: number; // Watcher 心跳过期阈值(5-3600)
501
+ toolProfile?: "full" | "chatgpt_core" | "chatgpt_direct" | "chatgpt_search";
502
+ enableDirectProfile?: boolean; // 是否启用 Direct 模式
503
+ enableRunTaskTool?: boolean; // 是否暴露 run_task 工具
504
+ directSessionsDir: string;
505
+ directSessionTtlSeconds: number; // 60-86400
506
+ directMaxPatchBytes: number;
507
+ directMaxFileBytes: number;
508
+ directAllowedCommands?: string[];
509
+ repoDirectAllowedCommands?: Record<string, string[]>;
510
+ tunnelClientPath?: string;
511
+ tunnelProxy?: {
512
+ scope: "shared" | "separate";
513
+ core: { mode: "environment" | "none" | "manual"; url?: string };
514
+ direct: { mode: "environment" | "none" | "manual"; url?: string };
515
+ };
516
+ httpPort?: number;
517
+ http?: { port?: number; host?: string; ownerTokenEnv?: string };
518
+ // ... 其他可选字段
519
+ }
520
+ ```
521
+
522
+ #### `TaskStatus`([src/tools/tasks/createTask.ts](../src/tools/tasks/createTask.ts))
523
+
524
+ ```typescript
525
+ type TaskStatus =
526
+ | "pending" | "running" | "collecting_artifacts"
527
+ | "done_by_agent" // agent 自报完成,待验收
528
+ | "accepted" | "rejected" | "needs_fix" | "blocked" // 验收状态
529
+ | "done" // legacy 终态
530
+ | "failed" | "failed_verification"
531
+ | "failed_scope_violation" | "failed_policy_violation"
532
+ | "failed_stale" | "orphaned" // 进程死亡/孤儿
533
+ | "canceled";
534
+ ```
535
+
536
+ #### `PatchWardenError`([src/errors.ts](../src/errors.ts))
537
+
538
+ ```typescript
539
+ class PatchWardenError extends Error {
540
+ constructor(
541
+ public readonly reason: string, // 机器可读的错误码,如 "workspace_path_escape"
542
+ message: string, // 人类可读描述
543
+ public readonly suggestion: string, // 修复建议
544
+ public readonly blocked = true, // 是否阻断操作
545
+ public readonly details: Record<string, unknown> = {}
546
+ );
547
+ }
548
+ ```
549
+
550
+ #### `ToolCatalogSnapshot`([src/tools/catalog/toolCatalog.ts](../src/tools/catalog/toolCatalog.ts))
551
+
552
+ ```typescript
553
+ interface ToolCatalogSnapshot {
554
+ server_version: string; // PATCHWARDEN_VERSION
555
+ schema_epoch: string; // TOOL_SCHEMA_EPOCH
556
+ tool_profile: ToolProfile;
557
+ tool_count: number;
558
+ tool_names: string[];
559
+ tool_manifest_sha256: string; // 工具清单哈希,用于漂移检测
560
+ }
561
+ ```
562
+
563
+ #### `ToolHandlerMap`([src/tools/dispatch/types.ts](../src/tools/dispatch/types.ts))
564
+
565
+ ```typescript
566
+ type ToolHandler = (args?: unknown) => Promise<{ content: Array<{ type: string; text: string }> }>;
567
+ type ToolHandlerMap = Record<string, ToolHandler>;
568
+ function toResult(data: unknown): { content: Array<{ type: "text"; text: string }> };
569
+ ```
570
+
571
+ ### 5.2 核心函数
572
+
573
+ #### 配置加载
574
+
575
+ ```typescript
576
+ // src/config.ts
577
+ function loadConfig(configPath?: string): PatchWardenConfig; // 单例加载
578
+ function reloadConfig(configPath?: string): PatchWardenConfig; // 强制重载
579
+ function getConfig(): PatchWardenConfig;
580
+ function getTasksDir(config: PatchWardenConfig): string;
581
+ function getPlansDir(config: PatchWardenConfig): string;
582
+ function getAssessmentsDir(config: PatchWardenConfig): string;
583
+ function resolveWorkspaceRoot(config: PatchWardenConfig): string;
584
+ function getRepoAllowedTestCommands(config, repoPath): string[];
585
+ function getRepoDirectAllowedCommands(config, repoPath): string[];
586
+ function normalizeRepoKey(value: string): string;
587
+ function comparablePath(value: string): string; // Windows 下以小写绝对路径比较 repo key
588
+ ```
589
+
590
+ #### 跨进程锁与原子存储(v1.6.1)
591
+
592
+ ```typescript
593
+ // src/utils/lockedJsonFile.ts
594
+ function withFileLockSync<T>(lockPath: string, fn: () => T): T;
595
+ function mutateLockedJsonFileSync<T>(
596
+ filePath: string,
597
+ mutation: (current: T) => { next: T; result?: unknown },
598
+ options?: { lockPath?: string; initialValue?: T }
599
+ ): unknown;
600
+
601
+ // src/utils/atomicFile.ts
602
+ function atomicWriteFileSync(path: string, data: string | Buffer): void;
603
+ function atomicWriteJsonFileSync(path: string, data: unknown): void;
604
+ ```
605
+
606
+ #### 安全守卫
607
+
608
+ ```typescript
609
+ // src/security/commandGuard.ts
610
+ function guardAgentCommand(agent: string, config: PatchWardenConfig): AllowedCommand;
611
+ function guardTestCommand(testCommand: string, config, repoPath?): string;
612
+ function guardDirectCommand(command: string, config, repoPath?): string;
613
+ function sanitizePromptArg(prompt: string): string;
614
+
615
+ // src/security/pathGuard.ts
616
+ function guardPath(requestedPath, workspaceRoot, allowedPrefix?): string;
617
+ function guardReadPath(requestedPath, workspaceRoot, allowedPrefix?): string;
618
+ function guardWorkspacePath(inputPath, workspaceRoot): string; // 用于 repo_path
619
+
620
+ // src/security/sensitiveGuard.ts
621
+ function isSensitivePath(filePath: string): boolean;
622
+ function guardSensitivePath(filePath: string): void;
623
+
624
+ // src/security/planGuard.ts
625
+ function guardPlanContent(title: string, content: string): void;
626
+
627
+ // src/security/riskEngine.ts
628
+ function assessRisk(input: RiskAssessmentInput): RiskAssessmentResult;
629
+ function collectRiskHints(input: RiskAssessmentInput): string[];
630
+
631
+ // src/security/runtimeGuard.ts
632
+ function guardRuntimeSelfModification(resolvedRepoPath: string): void;
633
+ // 使用平台感知的同路径/子路径比较,Windows 下忽略大小写
634
+
635
+ // src/security/contentRedaction.ts
636
+ function redactSensitiveContent(input: string): RedactionResult;
637
+ function redactSensitiveValue<T>(input: T): StructuredRedactionResult<T>;
638
+ ```
639
+
640
+ #### 任务执行
641
+
642
+ ```typescript
643
+ // src/runner/runTask.ts
644
+ async function runTask(taskId: string): Promise<TaskRunResult>;
645
+ // 编排完整生命周期,产出 16+ artifact
646
+ // 子阶段:prepareTask / executeAgent / runVerification / collectArtifacts / finalizeTask
647
+
648
+ // src/runner/watch.ts(脚本入口,部分导出供测试)
649
+ function acquireWatcherLock(lockFilePath?: string): void; // 单实例排他锁 + stale PID 接管
650
+ function releaseWatcherLock(lockFilePath?, instanceId?, pid?): boolean;
651
+ function createNonOverlappingRunner(operation, onSkipped?): () => Promise<boolean>;
652
+ // tick() 每 4 秒轮询 pending 任务,pre-flight 校验后调用 runTask
653
+
654
+ // src/runner/changeCapture.ts
655
+ async function captureRepoSnapshot(repoPath: string): Promise<RepoSnapshot>;
656
+ async function buildChangeArtifacts(repoPath, before, after): Promise<ChangeArtifacts>;
657
+ function extractExternalDirtyFiles(snapshot, repoPath, workspaceRoot): ExternalDirtyFile[];
658
+ function findNewExternalDirtyFiles(baseline, current): ExternalDirtyFile[];
659
+ async function buildArtifactManifest(changedFiles, repoPath, taskId?): Promise<ArtifactManifest>;
660
+ // 通过 nullDevice 在 Windows 使用 NUL、其他平台使用 /dev/null
661
+ // Windows 下使用小写路径键比较 before/after 快照、tracked/ignored/dirty
662
+ ```
663
+
664
+ #### MCP 工具
665
+
666
+ ```typescript
667
+ // src/tools/registry.ts
668
+ function getToolDefs(): ToolDef[]; // 生成完整工具定义
669
+ function registerTools(server: Server): void; // 绑定到 MCP Server
670
+ async function handleToolCall(name: string, args?): Promise<{ content }>;
671
+ function getToolCatalogSnapshot(): ToolCatalogSnapshot;
672
+ // dispatchMap 由 buildDispatchMap() 在模块加载时构造(配置无关)
673
+
674
+ // src/tools/dispatch/*.ts
675
+ export const coreHandlers: ToolHandlerMap; // 30+ 核心工具
676
+ function buildDiagnosticHandlers(dispatchTool): ToolHandlerMap; // 注入 audited dispatcher,避免循环依赖
677
+ export const directHandlers: ToolHandlerMap; // 9 个 Direct 工具
678
+ export const goalHandlers: ToolHandlerMap; // 12 个 Goal 工具
679
+ export const releaseHandlers: ToolHandlerMap; // 4 个 Release 工具
680
+ export const runTaskHandler: ToolHandler; // 条件注册
681
+
682
+ // src/tools/dispatch/validation.ts (v1.6.1)
683
+ function asString(value: unknown, field: string): string;
684
+ function asNumber(value: unknown, field: string): number;
685
+ function asBoolean(value: unknown, field: string): boolean;
686
+ function asStringArray(value: unknown, field: string): string[];
687
+ function asRecord(value: unknown, field: string): Record<string, unknown>;
688
+
689
+ // src/tools/tasks/createTask.ts
690
+ async function createTask(input: CreateTaskInput): Promise<CreateTaskResult>;
691
+ // 支持 assess_only 预评估与 execute 执行
692
+
693
+ // src/tools/tasks/runTaskLoop.ts
694
+ async function runTaskLoop(input: RunTaskLoopInput): Promise<RunTaskLoopOutput>;
695
+ // 安全编排入口,最多 5 轮迭代
696
+
697
+ // src/tools/diagnostics/auditTask.ts
698
+ function auditTask(taskId: string): AuditTaskOutput;
699
+ // 16+ 确定性检查,区分 confirmed_failures/possible_false_positives
700
+
701
+ // src/tools/tasks/waitForTask.ts
702
+ async function waitForTask(taskId: string, waitSeconds?: number): Promise<WaitResult>;
703
+ // 长轮询,返回 continuation_required 或终态证据
704
+
705
+ // src/tools/catalog/toolSearch.ts
706
+ function discoverTools(input, registry, tokenIssuer?, usageStatsProvider?): DiscoverToolsOutput;
707
+ function explainTool(input, registry, toolDefs?, tokenPeeker?): ExplainToolOutput | null;
708
+ function classifyQueryIntent(query): QueryIntent;
709
+ ```
710
+
711
+ #### Goal Session
712
+
713
+ ```typescript
714
+ // src/goal/goalStore.ts
715
+ function createGoal(repoPath, title, description, workspaceRoot?): { goal_id, goal_dir };
716
+ function listGoals(workspaceRoot?): GoalSummary[];
717
+ function readGoal(goalId, workspaceRoot?): GoalDetail;
718
+ function writeGoalStatus(goalId, status, workspaceRoot?): void;
719
+ function readGoalStatus(goalId, workspaceRoot?): GoalStatus;
720
+ function mutateGoalStatus<R>(goalId, mutation, workspaceRoot?): R;
721
+ async function mutateGoalStatusAsync<R>(goalId, mutation, workspaceRoot?): Promise<R>;
722
+ function generateGoalId(title: string, existingIds: string[]): string; // goal_{YYYYMMDD}_{slug}
723
+
724
+ // src/goal/goalGraph.ts
725
+ function suggestNextSubgoal(goalStatus: GoalStatus): SubgoalSuggestion;
726
+
727
+ // src/goal/worktreeManager.ts
728
+ function createWorktree(goalId, subgoalId, workspaceRoot): WorktreeInfo;
729
+ function mergeWorktree(worktreeId, workspaceRoot): MergeResult;
730
+ function discardWorktree(worktreeId, workspaceRoot): DiscardResult;
731
+ // 三类操作共享仓库级 lifecycle lock
732
+ ```
733
+
734
+ #### Direct Session
735
+
736
+ ```typescript
737
+ // src/direct/directSessionStore.ts
738
+ function generateDirectSessionId(): string; // direct_{YYYYMMDD_HHMMSS}_{randomHex32}
739
+ function createDirectSession(input: DirectSessionCreateInput): DirectSessionRecord;
740
+ function readDirectSession(sessionId: string): DirectSessionRecord;
741
+ function updateDirectSession(sessionId, patch): DirectSessionRecord;
742
+ function appendDirectSessionOperation(sessionId, operation): DirectSessionRecord; // 锁内 mutation + 原子替换
743
+ function finalizeDirectSessionRecord(sessionId, artifacts): DirectSessionRecord;
744
+ function validateDirectSessionFreshness(session): DirectSessionValidationResult;
745
+
746
+ // src/direct/directGuards.ts
747
+ function guardDirectSessionActive(session: DirectSessionRecord): void;
748
+ function guardDirectPath(filePath, resolvedRepoPath, workspaceRoot): string;
749
+ function guardDirectReadPath(session, requestedPath): string;
750
+ function guardDirectWritePath(session, requestedPath): string;
751
+ function guardDirectPatchSize(patchBytes: number): void;
752
+ function isBinaryFile(filePath: string): boolean;
753
+ ```
754
+
755
+ #### Discovery Token
756
+
757
+ ```typescript
758
+ // src/security/discoveryTokenStore.ts
759
+ function issueToken(input: IssueTokenInput): string; // dst_{YYYYMMDD}_{randomHex12}
760
+ function consumeToken(tokenId: string): DiscoveryTokenRecord; // 单次使用,消费后删除
761
+ function peekToken(tokenId: string): DiscoveryTokenRecord | null;
762
+ function revokeToken(tokenId: string): boolean;
763
+ // 签发时清理过期记录,活动 token 上限为 1024
764
+ ```
765
+
766
+ #### Control Center
767
+
768
+ ```typescript
769
+ // src/control/server.ts
770
+ function startServer(): Server; // 启动 HTTP 服务,绑定 127.0.0.1
771
+
772
+ // src/control/runtime.ts
773
+ function probeHealthStatus(url: string): Promise<HealthProbe>;
774
+ function classifyStaleTask(task, watcher, nowMs): StaleClassification;
775
+ function recordEvent(type: string, payload: object): void;
776
+ function readEvents(limit: number): EventRecord[];
777
+ function isValidTaskId(taskId: string): boolean;
778
+ function isValidDirectSessionId(sessionId: string): boolean;
779
+ // append 与 trim 在同一跨进程文件锁内执行,裁剪结果使用原子替换提交
780
+
781
+ // src/control/middleware/auth.ts
782
+ function checkControlToken(req: IncomingMessage): boolean;
783
+ // 使用 crypto.timingSafeEqual,并先校验 Buffer 长度
784
+ ```
785
+
786
+ ---
787
+
788
+ ## 6. 数据流与状态机
789
+
790
+ ### 6.1 标准任务工作流
791
+
792
+ ```text
793
+ 1. health_check → 确认版本、工作区、Watcher、工具目录
794
+ 2. list_agents → 确认本地 Agent 命令可用
795
+ 3. list_workspace → 确定 repo_path
796
+ 4. save_plan → 保存计划(或使用 inline_plan/template)
797
+ 5. create_task → 明确 Agent、仓库、验证命令
798
+ 6. wait_for_task → 短任务用 timeout_seconds:25;长任务用 list_tasks 轮询
799
+ 7. get_task_summary(compact) → 先看有界结构化总结
800
+ 8. get_result_json / get_diff / get_test_log → 按需查看细节
801
+ 9. audit_task → 独立核对执行结果
802
+ 10. 人工决定接受/提交/发布
803
+ ```
804
+
805
+ ### 6.2 任务状态机
806
+
807
+ ```text
808
+ pending ──(Watcher 拉取)──▶ running ──(agent 退出)──▶ collecting_artifacts
809
+
810
+ ┌───────────────────────────────┤
811
+ ▼ ▼
812
+ done_by_agent failed / failed_verification
813
+ │ │
814
+ ┌───────────────┼───────────────┐ │
815
+ ▼ ▼ ▼ ▼
816
+ accepted rejected needs_fix failed_scope_violation
817
+ failed_policy_violation
818
+ failed_stale / orphaned
819
+ canceled
820
+ ```
821
+
822
+ ### 6.3 任务产物
823
+
824
+ | 文件 | 用途 |
825
+ | --- | --- |
826
+ | `status.json` | 当前状态、阶段、心跳和错误信息 |
827
+ | `progress.md` | Agent 写入的进度记录 |
828
+ | `result.md` | 人类可读的执行报告 |
829
+ | `result.json` | 结构化结果、路径、变更、警告和后续建议 |
830
+ | `diff.patch` | 最多 20 MiB 的任务差异证据;疑似凭据在落盘前脱敏,超限显式标记截断 |
831
+ | `artifact_manifest.json` | 构建或发布产物的路径、类型、大小与 SHA-256 |
832
+ | `file-stats.json` | 文件级增删统计 |
833
+ | `verify.json` | 每条独立验证命令的结构化记录 |
834
+ | `verify.log` | 独立验证的可读日志 |
835
+ | `test.log` | Agent 执行过程中产生的测试输出 |
836
+ | `git-before.json` / `git-after.json` | 任务前后仓库快照 |
837
+ | `workspace-before.json` | 任务前工作区快照(外部脏文件基线) |
838
+ | `changed-files.json` | 变更文件列表 |
839
+ | `independent-review.md` | `audit_task` 写入的独立审计报告 |
840
+ | `runtime.json` | 子进程 PID、心跳、deadline |
841
+ | `error.log` | 错误日志 |
842
+ | `post-task-cleanup.json` | 清理报告 |
843
+ | `reconcile.json` | Control Center reconcile 写入的注解 |
844
+ | `stdout.log` / `stderr.log` | Agent 与验证子进程的有界脱敏输出 |
845
+
846
+ 资源边界也适用于读取和持续日志:普通任务产物受 `maxReadFileBytes` 限制,diff/summary/log tail 使用有界前缀或尾部读取;`audit_task` 的 Markdown 扫描上限为 200 个文件、4 MiB 总内容。`invocation.log` 与 `reconcile.log` 在跨进程锁内有界追加,达到上限后保留最近内容并写入截断标记,避免无界内存和磁盘增长。
847
+
848
+ ### 6.4 run_task_loop 编排流
849
+
850
+ `run_task_loop` 是 v1.2 引入的安全编排入口,它只组合现有工具,不绕过任何守卫:
851
+
852
+ ```text
853
+ runTaskLoop(input)
854
+
855
+ ├─ guardWorkspacePath(repo_path)
856
+ ├─ resolveAgentRouting(agent="auto" → recommendAgentForTask)
857
+ ├─ createLineageId + 初始化 lineage 记录
858
+
859
+ └─ for round in 1..max_iterations:
860
+ ├─ create_task(template, goal, verify_commands, agent)
861
+ ├─ waitForTask(task_id, timeout)
862
+ ├─ safeResult + safeTestSummary + safeAudit
863
+
864
+ ├─ if high_risk && stop_on_high_risk → break
865
+ ├─ if failed_verification && auto_fix_tests → 下一轮用 fix_tests 模板
866
+ ├─ if done_by_agent/accepted → break
867
+ └─ writeTaskLineage(round_record)
868
+
869
+ ├─ if direct_verify: createDirectSession → runDirectVerificationBundle → safeFinalize → safeAudit
870
+ └─ writeTaskLineage(final) → 返回 SafeTaskLineage
871
+ ```
872
+
873
+ ---
874
+
875
+ ## 7. 依赖关系
876
+
877
+ ### 7.1 外部依赖
878
+
879
+ **主仓**(`package.json`):
880
+
881
+ ```json
882
+ {
883
+ "dependencies": {
884
+ "@modelcontextprotocol/sdk": "^1.29.0"
885
+ },
886
+ "devDependencies": {
887
+ "@types/node": "^18.19.0",
888
+ "typescript": "^6.0.3"
889
+ },
890
+ "engines": { "node": ">=18.0.0" }
891
+ }
892
+ ```
893
+
894
+ **Desktop 子包**(`desktop/package.json`):
895
+
896
+ ```json
897
+ {
898
+ "dependencies": {
899
+ "jsonc-parser": "3.3.1",
900
+ "smol-toml": "1.6.1",
901
+ "yaml": "2.8.3"
902
+ },
903
+ "devDependencies": {
904
+ "@types/node": "^24.0.0",
905
+ "electron": "43.1.1",
906
+ "electron-builder": "26.15.3"
907
+ }
908
+ }
909
+ ```
910
+
911
+ 主仓运行时**仅依赖 MCP SDK**,所有其他功能使用 Node.js 内置模块:
912
+
913
+ - `node:fs` / `node:path` / `node:crypto` / `node:http` / `node:child_process` / `node:os` / `node:url` / `node:net` / `node:timers/promises`
914
+
915
+ ### 7.2 模块依赖图(简化)
916
+
917
+ ```text
918
+ index.ts / httpServer.ts
919
+
920
+
921
+ tools/registry.ts ──────────┬── tools/dispatch/*.ts ── tools/tasks/*
922
+ │ │ ├── tools/diagnostics/*
923
+ │ │ ├── tools/workspace/*
924
+ │ │ ├── tools/discovery/*
925
+ │ │ ├── tools/catalog/*
926
+ │ │ ├── tools/goals/*
927
+ │ │ ├── direct/* (Direct 工具)
928
+ │ │ ├── goal/* (Goal 工具)
929
+ │ │ └── release/releaseGate.ts
930
+
931
+
932
+ config.ts ──── security/* (所有守卫)
933
+
934
+
935
+ runner/watch.ts ──▶ runner/runTask.ts ──▶ runner/changeCapture.ts
936
+ ├── runner/agentInvocation.ts
937
+ ├── runner/postTaskCleanup.ts
938
+ ├── runner/simpleProcess.ts
939
+ ├── runner/processSecurity.ts
940
+ └── runner/taskStatusStore.ts (锁内 mutate)
941
+
942
+ controlCenter.ts ──▶ control/server.ts ──┬── control/shared.ts
943
+ ├── control/runtime.ts
944
+ ├── control/routeTable.ts
945
+ ├── control/middleware/{auth,static}.ts
946
+ └── control/routes/*.ts (10 个领域)
947
+
948
+ desktop/main.ts ──▶ desktop/{agent-adapters,model-discovery,config-store,
949
+ tunnel-provisioner,runtime-settings,runtime-root,
950
+ backend-probe,backend-lifecycle,child-environment}.ts
951
+ └─ utilityProcess.fork ──▶ dist/controlCenter.js (主仓)
952
+
953
+ utils/lockedJsonFile.ts ◀── 所有跨进程 mutation 站点
954
+ utils/atomicFile.ts ◀── 所有 .tmp-{pid} + rename 站点
955
+ utils/platform.ts ◀── runtimeGuard / pathGuard / config
956
+ ```
957
+
958
+ ### 7.3 关键依赖关系
959
+
960
+ - **所有写操作** → 必须经 `security/*` 守卫
961
+ - **`createTask`** → `riskEngine.assessRisk` → `pathGuard` + `commandGuard` + `planGuard` + `runtimeGuard` + `sensitiveGuard`
962
+ - **`runTask`** → `agentInvocation.buildAgentInvocation` → `commandGuard.guardAgentCommand`
963
+ - **`runTask`** → `changeCapture.captureRepoSnapshot` + `buildChangeArtifacts`
964
+ - **`runTask`** → `claimPendingTask`(taskStatusStore 锁内单次领取)
965
+ - **`runTaskLoop`** → 组合 `createTask` + `waitForTask` + `safeViews` + `auditTask`,不直接调用 `runTask`
966
+ - **`watch.ts`** → pre-flight 调用 `guardWorkspacePath` + `guardAgentCommand` + `guardTestCommand` 后才调用 `runTask`
967
+ - **`invokeDiscoveredTool`** → `discoveryTokenStore.consumeToken` + `toolInvocationGuard.checkInvocation` + 实际 handler 内二次校验
968
+ - **`registry.ts`** → 模块加载时 `buildDispatchMap()` 一次性合并 5 个 handler map(配置热重载后不刷新,需重连)
969
+ - **`control/server.ts`** → POST 路由统一前置 `checkControlToken`,route 文件本身不重复校验
970
+ - **`desktop/main.ts`** → `utilityProcess.fork` 拉起主仓 `dist/controlCenter.js`,通过 `127.0.0.1:8090` 通信
971
+ - **所有持久化状态 mutation** → 经 `utils/lockedJsonFile.ts` 跨进程锁 + `utils/atomicFile.ts` 原子替换
972
+
973
+ ---
974
+
975
+ ## 8. 项目运行方式
976
+
977
+ ### 8.1 环境要求
978
+
979
+ - Node.js ≥ 18
980
+ - npm
981
+ - Git(可选,但无 Git 无法生成可靠 `git.diff`)
982
+ - 至少一个本地编程 Agent(OpenCode / Codex / Claude Code / Gemini / Copilot / Qwen / Kimi / Aider)
983
+ - Windows Tunnel 模式还需 `tunnel-client.exe`、Tunnel ID、运行时 API Key
984
+ - 桌面打包需要 Windows x64 + Electron
985
+
986
+ ### 8.2 从源码运行(推荐)
987
+
988
+ ```powershell
989
+ git clone https://github.com/jiezeng2004-design/PatchWarden.git
990
+ cd .\PatchWarden
991
+ npm.cmd ci
992
+ npm.cmd run build
993
+ Copy-Item .\examples\config.example.json .\patchwarden.config.json
994
+ ```
995
+
996
+ 编辑 `patchwarden.config.json`,至少修改 `workspaceRoot`、`agents`、`allowedTestCommands`。
997
+
998
+ ### 8.3 配置文件
999
+
1000
+ 配置路径通过 `PATCHWARDEN_CONFIG` 环境变量指定:
1001
+
1002
+ ```powershell
1003
+ $env:PATCHWARDEN_CONFIG = "D:\path\to\patchwarden.config.json"
1004
+ ```
1005
+
1006
+ 查找顺序:
1007
+ 1. `PATCHWARDEN_CONFIG` 环境变量指定的路径
1008
+ 2. `{cwd}/patchwarden.config.json`
1009
+ 3. `{cwd}/.patchwarden.json`
1010
+
1011
+ 最小配置示例:
1012
+
1013
+ ```json
1014
+ {
1015
+ "workspaceRoot": "D:/ai_agent/codex_program",
1016
+ "plansDir": ".patchwarden/plans",
1017
+ "tasksDir": ".patchwarden/tasks",
1018
+ "toolProfile": "full",
1019
+ "agents": {
1020
+ "opencode": { "command": "opencode", "args": ["run", "{prompt}"], "envAllowlist": [] },
1021
+ "codex": { "command": "codex", "args": ["exec", "--cd", "{repo}", "{prompt}"], "envAllowlist": [] }
1022
+ },
1023
+ "allowedTestCommands": ["npm test", "npm run build", "npm run lint", "pytest"],
1024
+ "maxReadFileBytes": 200000,
1025
+ "defaultTaskTimeoutSeconds": 900,
1026
+ "maxTaskTimeoutSeconds": 3600,
1027
+ "watcherStaleSeconds": 30,
1028
+ "httpPort": 7331
1029
+ }
1030
+ ```
1031
+
1032
+ `agents` 是显式执行白名单;未登记的 Agent 不可启动。Agent 子进程默认不继承 provider 环境变量,仅转发对应 `envAllowlist` 中明确列出的变量,Tunnel/HTTP owner credential 始终禁止转发。`workspaceRoot` 在配置加载时 fail closed:路径缺失、不可访问、不是目录,或指向盘符根目录、用户主目录、Desktop、Downloads、Documents 时都会拒绝启动。
1033
+
1034
+ ### 8.4 核心命令
1035
+
1036
+ | 命令 | 用途 |
1037
+ | --- | --- |
1038
+ | `npm.cmd run build` | TypeScript 编译到 `dist/` |
1039
+ | `npm.cmd run doctor` | 只读诊断(检查 15+ 项) |
1040
+ | `npm.cmd run doctor:ci` | CI 诊断(允许默认配置) |
1041
+ | `npm.cmd run watch` | 启动 Watcher(轮询 pending 任务) |
1042
+ | `npm.cmd start` | 启动 stdio MCP Server |
1043
+ | `npm.cmd run start:http` | 启动 HTTP MCP Server(127.0.0.1:7331) |
1044
+ | `npm.cmd run start:control` | 启动 Control Center Dashboard(127.0.0.1:8090) |
1045
+ | `npm.cmd test` | 完整测试链(smoke + unit + lifecycle + doctor + tunnel + watcher + control + mcp + brand) |
1046
+ | `npm.cmd run test:unit` | 仅单元测试 |
1047
+ | `npm.cmd run test:mcp` | MCP 烟雾测试 |
1048
+ | `npm.cmd run test:http-mcp` | HTTP MCP 烟雾测试 |
1049
+ | `npm.cmd run check:tool-manifest` | 工具 Manifest 校验 |
1050
+ | `npm.cmd run pack:clean` | 打包清理 |
1051
+ | `npm.cmd run verify:package` | 包清单校验 |
1052
+ | `npm.cmd run desktop:install` | 安装 desktop 子包依赖 |
1053
+ | `npm.cmd run desktop:test` | desktop 单元测试 |
1054
+ | `npm.cmd run desktop:package` | 打包 Windows NSIS 安装包 + ZIP |
1055
+ | `npm.cmd run desktop:preflight` | 桌面发布前预检(含 unpacked UI/单实例验收) |
1056
+ | `npm.cmd run desktop:preflight:release` | 干净 checkout 的发布前预检(拒绝未提交路径) |
1057
+
1058
+ ### 8.5 启动顺序
1059
+
1060
+ 完整链路需要三个独立进程:
1061
+
1062
+ **终端 1 — Watcher**:
1063
+
1064
+ ```powershell
1065
+ $env:PATCHWARDEN_CONFIG = (Resolve-Path .\patchwarden.config.json)
1066
+ npm.cmd run watch
1067
+ ```
1068
+
1069
+ **终端 2 — MCP Server**(stdio 或 HTTP 二选一):
1070
+
1071
+ ```powershell
1072
+ # stdio(供 OpenCode/Codex 直接连)
1073
+ $env:PATCHWARDEN_CONFIG = (Resolve-Path .\patchwarden.config.json)
1074
+ npm.cmd start
1075
+
1076
+ # 或 HTTP(供 Tunnel 或远程客户端)
1077
+ npm.cmd run start:http
1078
+ ```
1079
+
1080
+ **终端 3 — Control Center**(可选):
1081
+
1082
+ ```powershell
1083
+ npm.cmd run start:control
1084
+ ```
1085
+
1086
+ ### 8.6 Windows 一键启动
1087
+
1088
+ ```text
1089
+ PatchWarden.cmd start core # 启动 Core Agent 模式(chatgpt_core + Watcher + Tunnel)
1090
+ PatchWarden.cmd start direct # 启动 Direct 模式(chatgpt_direct + Tunnel,无 Watcher)
1091
+ PatchWarden.cmd stop all # 停止所有受控进程
1092
+ PatchWarden.cmd restart all # 重启所有受控进程
1093
+ PatchWarden.cmd status all # 查看运行状态
1094
+ PatchWarden.cmd health # 深度健康检查
1095
+ PatchWarden.cmd kill all # 强制清理
1096
+ scripts\launchers\PatchWarden-Desktop.cmd # 启动 Desktop 应用(托盘 + Control Center)
1097
+ ```
1098
+
1099
+ ### 8.7 MCP 客户端接入
1100
+
1101
+ **OpenCode**(`%USERPROFILE%\.config\opencode\opencode.jsonc`):
1102
+
1103
+ ```jsonc
1104
+ {
1105
+ "mcp": {
1106
+ "patchwarden": {
1107
+ "type": "local",
1108
+ "command": ["node", "D:/path/to/PatchWarden/dist/index.js"],
1109
+ "environment": {
1110
+ "PATCHWARDEN_CONFIG": "D:/path/to/PatchWarden/patchwarden.config.json",
1111
+ "PATCHWARDEN_TOOL_PROFILE": "full"
1112
+ },
1113
+ "enabled": true
1114
+ }
1115
+ }
1116
+ }
1117
+ ```
1118
+
1119
+ **Codex**(`%USERPROFILE%\.codex\config.toml`):
1120
+
1121
+ ```toml
1122
+ [mcp_servers.patchwarden]
1123
+ command = "node"
1124
+ args = ["D:\\path\\to\\PatchWarden\\dist\\index.js"]
1125
+
1126
+ [mcp_servers.patchwarden.env]
1127
+ PATCHWARDEN_CONFIG = "D:\\path\\to\\patchwarden.config.json"
1128
+ PATCHWARDEN_TOOL_PROFILE = "full"
1129
+ ```
1130
+
1131
+ ### 8.8 HTTP MCP 端点
1132
+
1133
+ | 端点 | 方法 | 用途 |
1134
+ | --- | --- | --- |
1135
+ | `/mcp` | POST | MCP 请求(需 owner token) |
1136
+ | `/healthz` | GET | 健康检查 |
1137
+ | `/readyz` | GET | 就绪检查(不就绪返回 503) |
1138
+ | `/admin/tasks/:id/accept` | POST | 人工接受任务 |
1139
+ | `/admin/tasks/:id/reject` | POST | 人工拒绝任务 |
1140
+ | `/admin/tasks/:id/acceptance` | GET | 读取验收状态 |
1141
+
1142
+ ### 8.9 Control Center API 端点(127.0.0.1:8090)
1143
+
1144
+ | 端点 | 方法 | 用途 |
1145
+ | --- | --- | --- |
1146
+ | `/api/status` | GET | 主轮询端点(10 路并发探测 + 6 类建议) |
1147
+ | `/api/tasks` | GET | 任务列表(支持 repo_path/status/acceptance_status/agent/warning_type 过滤) |
1148
+ | `/api/tasks/:id` | GET | 任务详情(含配置上限内的脱敏 diff/test_log) |
1149
+ | `/api/tasks/:id/safe-result` | GET | safe 任务结果摘要 |
1150
+ | `/api/tasks/:id/safe-audit` | GET | safe 审计摘要 |
1151
+ | `/api/tasks/:id/safe-test-summary` | GET | safe 测试摘要 |
1152
+ | `/api/tasks/:id/safe-diff-summary` | GET | safe 差异摘要 |
1153
+ | `/api/tasks/stale` | GET | stale 任务列表 |
1154
+ | `/api/tasks/:id/reconcile` | POST | reconcile 任务状态 |
1155
+ | `/api/tasks/:id/audit` | POST | 运行 audit_task |
1156
+ | `/api/tasks/:id/hide-stale` | POST | 隐藏 stale 任务 |
1157
+ | `/api/direct-sessions` | GET | Direct session 列表 |
1158
+ | `/api/direct-sessions/:id/finalize` | POST | finalize Direct session |
1159
+ | `/api/direct-sessions/:id/audit` | POST | audit Direct session |
1160
+ | `/api/direct-sessions/:id/hide` | POST | 隐藏 Direct session |
1161
+ | `/api/lineages` | GET | lineage 列表 |
1162
+ | `/api/lineages/:id` | GET | lineage 详情 |
1163
+ | `/api/evidence-packs` | GET | 证据包列表 |
1164
+ | `/api/evidence-packs/:id/export` | POST | 导出证据包 |
1165
+ | `/api/workspace` | GET | 工作区信息 |
1166
+ | `/api/workspace/repos` | GET | 工作区 repo 列表 |
1167
+ | `/api/workspace/:repo/status` | GET | 单 repo git status |
1168
+ | `/api/project-policy` | GET | 项目策略 |
1169
+ | `/api/release/status` | GET | release 就绪状态 |
1170
+ | `/api/diagnostics` | GET | 诊断信息(脱敏) |
1171
+ | `/api/warnings` | GET | 警告聚合(7 类) |
1172
+ | `/api/audit` | GET | 审计聚合 |
1173
+ | `/api/logs/:category` | GET | 日志 tail(core/direct/watcher/control-center) |
1174
+ | `/api/events` | GET | 事件时间线 |
1175
+ | `/api/control-center-status` | GET | Control Center 自身状态 |
1176
+ | `/api/start-all` `/stop-all` `/restart-all` | POST | 进程管理(需 control token) |
1177
+ | `/api/core/start` `/core/stop` | POST | Core 进程管理 |
1178
+ | `/api/direct/start` `/direct/stop` | POST | Direct 进程管理 |
1179
+
1180
+ ---
1181
+
1182
+ ## 9. 安全设计
1183
+
1184
+ ### 9.1 安全分层
1185
+
1186
+ ```text
1187
+ ┌─────────────────────────────────────────────────┐
1188
+ │ MCP 客户端(ChatGPT/Codex/OpenCode) │ ← 模型指令被视为不可信输入
1189
+ ├─────────────────────────────────────────────────┤
1190
+ │ tools/registry.ts │ ← 工具注册 + Profile 过滤
1191
+ │ tools/dispatch/*.ts │ ← 按领域分派
1192
+ │ tools/dispatch/validation.ts (v1.6.1) │ ← unknown → 类型守卫
1193
+ │ toolInvocationGuard (8 项调用前校验) │
1194
+ ├─────────────────────────────────────────────────┤
1195
+ │ security/pathGuard ← 路径横移防护 │
1196
+ │ security/commandGuard ← 命令白名单 │
1197
+ │ security/sensitiveGuard ← 敏感文件阻断 │
1198
+ │ security/planGuard ← 计划内容审查 │
1199
+ │ security/runtimeGuard ← 运行时自修改阻断 │
1200
+ │ security/riskEngine ← 综合风险评估 │
1201
+ ├─────────────────────────────────────────────────┤
1202
+ │ runner/runTask.ts ← 心跳/超时/cancel │
1203
+ │ runner/changeCapture.ts ← 作用域违规检测 │
1204
+ │ runner/postTaskCleanup.ts ← 受控清理 │
1205
+ │ runner/taskStatusStore.ts ← 锁内单次领取 │
1206
+ ├─────────────────────────────────────────────────┤
1207
+ │ utils/lockedJsonFile.ts ← 跨进程 mutation 锁 │
1208
+ │ utils/atomicFile.ts ← .tmp-{pid} + rename │
1209
+ │ security/contentRedaction ← 输出脱敏 │
1210
+ │ tools/diagnostics/safeViews.ts ← safe_* 有界摘要 │
1211
+ └─────────────────────────────────────────────────┘
1212
+ ```
1213
+
1214
+ ### 9.2 安全不变量
1215
+
1216
+ PatchWarden 的硬性约束(在 `watch.ts` 与 `runTask.ts` 中声明):
1217
+
1218
+ - `repo_path` 必须在 `workspaceRoot` 内,不能通过 `..` 跳出
1219
+ - Agent 必须在 `agents` 配置白名单中,命令来自本地配置而非模型输入
1220
+ - `test_command` / `verify_commands` 必须逐字匹配全局或仓库专属白名单
1221
+ - 每个任务最多运行一次(无自动重试循环);任务领取使用同目录硬链接锁 + 原子 JSON 替换
1222
+ - 不自动 commit、不自动 push、不删除文件、不发布、不重置仓库
1223
+ - 敏感文件名(`.env`/SSH 私钥/`credentials`/`cookies` 等)默认不可读,`.patchwarden/` 不构成豁免
1224
+ - 任务产物中的疑似密钥值会被脱敏
1225
+ - Goal 状态 mutation 与 worktree create/merge/discard 都使用跨进程锁;Goal 的非空子目标全部 accepted 后自动完成
1226
+ - 子目标任务只能使用 Goal 记录的 `repo_path`,仓库不一致时拒绝创建
1227
+ - audit、任务产物和日志读取/追加都有明确的字节、行数或文件数预算
1228
+ - HTTP Server 只绑定 `127.0.0.1`,并拒绝非回环 Host(防 DNS rebinding)
1229
+ - Control Center POST 路由统一前置 `x-patchwarden-control-token` 校验,使用常量时间比较
1230
+ - Windows DPAPI 加密 Tunnel 凭据,不写入仓库
1231
+ - Desktop 应用 `contextIsolation` + `sandbox` + IPC sender 校验;IPC / JSON / model-discovery / tunnel-process 数据从 `unknown` 校验
1232
+
1233
+ ### 9.3 作用域违规检测
1234
+
1235
+ `changeCapture.ts` 实现"双快照 + 外部脏文件基线"机制:
1236
+
1237
+ 1. 任务执行前:`captureRepoSnapshot(repoPath)` + `extractExternalDirtyFiles(workspaceSnapshot)` 建立基线
1238
+ 2. 任务执行后:`captureRepoSnapshot(repoPath)` + `buildChangeArtifacts`
1239
+ 3. `findNewExternalDirtyFiles(baseline, current)` 只标记任务期间**新增**的外部脏文件
1240
+ 4. 任务期间新增越界改动 → `failed_scope_violation`,写入 rollback 计划但**不自动回滚**
1241
+ 5. `change_policy: "no_changes"` 模式下出现任何改动 → `failed_policy_violation`
1242
+ 6. Windows 下使用小写路径键比较 before/after 快照,避免大小写敏感目录碰撞
1243
+
1244
+ ### 9.4 capability 模型(v0.8.1)
1245
+
1246
+ `invoke_discovered_tool` 采用"discover 发 token、invoke 凭 token 调用"的模型:
1247
+
1248
+ ```text
1249
+ discover_tools(query) ──▶ issueToken(toolName, risk, profile)
1250
+
1251
+
1252
+ invoke_discovered_tool(toolName, args, discoveryToken)
1253
+
1254
+ ┌───────────────┤
1255
+ ▼ ▼
1256
+ consumeToken toolInvocationGuard.checkInvocation
1257
+ (单次使用) (8 项校验)
1258
+
1259
+
1260
+ handler 内二次校验
1261
+ (如 commandGuard)
1262
+ ```
1263
+
1264
+ ### 9.5 需要保护的本地路径
1265
+
1266
+ | 路径 | 内容 | 是否应提交 Git |
1267
+ | --- | --- | --- |
1268
+ | `patchwarden.config.json` | 私人路径、Agent 和命令白名单 | 否 |
1269
+ | `.patchwarden/` | 计划、任务、差异和日志 | 否 |
1270
+ | `%APPDATA%\patchwarden` | DPAPI 加密的 Tunnel 凭据 | 否 |
1271
+ | `%LOCALAPPDATA%\patchwarden` | 运行时状态和隔离配置 | 否 |
1272
+ | `%LOCALAPPDATA%\PatchWarden` | Desktop 应用 userData | 否 |
1273
+
1274
+ ---
1275
+
1276
+ ## 10. 测试与发布
1277
+
1278
+ ### 10.1 测试体系
1279
+
1280
+ | 测试类型 | 命令 | 覆盖范围 |
1281
+ | --- | --- | --- |
1282
+ | 完整测试链 | `npm.cmd test` | smoke + unit + lifecycle + doctor + tunnel + watcher + control + mcp + brand |
1283
+ | 单元测试 | `npm.cmd run test:unit` | `src/test/unit/*.test.ts`(45+ 测试文件,按领域分组) |
1284
+ | MCP 烟雾测试 | `npm.cmd run test:mcp` | stdio MCP 工具调用 |
1285
+ | HTTP MCP 烟雾测试 | `npm.cmd run test:http-mcp` | HTTP MCP 端点 |
1286
+ | 生命周期测试 | `npm.cmd run test:lifecycle` | 任务完整生命周期 |
1287
+ | Doctor 烟雾测试 | `npm.cmd run test:doctor` | 诊断脚本 |
1288
+ | Tunnel 监督测试 | `npm.cmd run test:tunnel-supervisor` | Tunnel 启动器 |
1289
+ | Watcher 监督测试 | `npm.cmd run test:watcher-supervisor` | Watcher 心跳与所有权 |
1290
+ | Control 烟雾测试 | `npm.cmd run test:control` | Control Center API |
1291
+ | 工具 Manifest 校验 | `npm.cmd run check:tool-manifest` | 工具清单一致性 |
1292
+ | 品牌检查 | `npm.cmd run check:brand` | 品牌命名一致性 |
1293
+ | Desktop 测试 | `npm.cmd run desktop:test` | 桌面应用单元测试 |
1294
+
1295
+ v1.6.1 新增/扩展的覆盖:
1296
+
1297
+ - `runtime-guard-windows.test.ts`:运行时自修改与 Windows 路径比较
1298
+ - `change-capture-null-device.test.ts`:跨平台空设备
1299
+ - `runner/simple-process.test.ts`:子进程输出与日志流完成语义
1300
+ - `runner/run-task-claim.test.ts`、`runner/task-status-store.test.ts`:任务单次领取及取消竞态
1301
+ - `watcher-lock.test.ts`、`watcher-runtime.test.ts`:Watcher 单实例、stale lock 与空闲常驻
1302
+ - `workspace/apply-patch.test.ts`:Direct 原子替换后的结果与临时文件清理
1303
+ - `direct/direct-guards.test.ts`、`security/sensitive-guard.test.ts`、`workspace/sync-file.test.ts`:junction/symlink、ADS、敏感路径段和 Direct session 状态
1304
+ - `runner/agent-invocation.test.ts`:原生 OpenCode、已知 npm shim、未知 wrapper 拒绝
1305
+ - Desktop 测试覆盖 agent-detection / backend-lifecycle / backend-probe / child-environment / config-store / model-discovery / runtime-root / runtime-settings / tunnel-provisioner / ui-contract
1306
+
1307
+ ### 10.2 发布验证流程
1308
+
1309
+ 发布前必须依次完成:
1310
+
1311
+ ```powershell
1312
+ npm.cmd run build
1313
+ npm.cmd test
1314
+ npm.cmd run test:mcp
1315
+ npm.cmd run test:http-mcp
1316
+ npm.cmd run doctor
1317
+ npm.cmd run check:tool-manifest
1318
+ npm.cmd run check:brand
1319
+ npm.cmd run test:tunnel-supervisor
1320
+ npm.cmd run test:watcher-supervisor
1321
+ npm.cmd run pack:clean
1322
+ npm.cmd run verify:package
1323
+ ```
1324
+
1325
+ 发布后还需分别核验:
1326
+ - npm Registry(`patchwarden` 包)
1327
+ - GitHub Release 与 Tag
1328
+ - `dist-tags.latest`
1329
+
1330
+ ### 10.3 发布门控(Release Gate)
1331
+
1332
+ `releaseGate.ts` 实现五阶段顺序校验:
1333
+
1334
+ | 阶段 | 检查内容 | 远程? |
1335
+ | --- | --- | --- |
1336
+ | `local_ready` | 本地构建、版本、Git 状态、配置 | 否 |
1337
+ | `packed_ready` | 打包产物完整性、SHA-256 | 否 |
1338
+ | `published_verified` | npm Registry 包与版本存在 | 是(`node:https` GET) |
1339
+ | `github_release_verified` | GitHub Release 与资产存在 | 是(`node:https` GET) |
1340
+ | `ci_verified` | CI 工作流状态 | 是(`node:https` GET) |
1341
+
1342
+ 远程阶段网络错误返回 `not_checked`(不是 `failed`),不执行 shell 命令。
1343
+
1344
+ ### 10.4 打包排除
1345
+
1346
+ `pack:clean` 只重建 `release/package/` staging 和根目录的 tar/zip 包,不删除
1347
+ `release/desktop*` 产物;它与 `verify:package` 都会排除:
1348
+
1349
+ - `node_modules/`
1350
+ - `.patchwarden/`
1351
+ - `*.log`
1352
+ - `.env`
1353
+ - `patchwarden.config.json`
1354
+ - 本地凭据和运行时状态
1355
+
1356
+ ### 10.5 Desktop 打包
1357
+
1358
+ ```powershell
1359
+ npm.cmd run desktop:install # 安装 desktop 依赖(隔离缓存)
1360
+ npm.cmd run desktop:test # 运行桌面单元测试
1361
+ npm.cmd run desktop:package # 打包:先 stage 主仓 dist/,再 electron-builder
1362
+ ```
1363
+
1364
+ 输出到 `release/desktop/`:NSIS 安装包 + 免安装 ZIP + SHA256 校验和清单。首版不含自动更新或代码签名,Windows SmartScreen 可能提示未知发布者。`desktop:preflight` 会使用唯一目录完成 clean build、完整单测、桌面测试、npm 包面、Electron 目录包、仓库外 26-tool manifest 与隔离 unpacked UI/单实例验收,并生成 `preflight-report.json` / `preflight-report.md`。
1365
+
1366
+ ---
1367
+
1368
+ ## 11. 现有缺陷与改进建议
1369
+
1370
+ > 以下按严重性分级。CRITICAL 涉及安全/数据损坏;HIGH 影响功能;MEDIUM 影响可靠性/代码质量;LOW 影响可维护性。
1371
+ > 本节在 v1.6.1 复核(2026-07-20)后重新整理:标记 "✅ 已修复" 的条目已按当前源码与仓库内对应测试核对,不再属于开放缺陷;标记 "🟡 开放" 的条目仍需处理;标记 "ℹ️ 非缺陷" 的条目经复核不构成问题。
1372
+
1373
+ ### 11.1 跨平台缺陷(Windows + POSIX)
1374
+
1375
+ #### ✅ 11.1.1 `runtimeGuard.ts` 的 Windows 路径判断 — 已修复
1376
+
1377
+ - **现状**:[src/security/runtimeGuard.ts](../src/security/runtimeGuard.ts) 从模块位置解析 package root,并通过 `isSamePath`/`isPathChildOf` 做平台感知的父子路径判断,不再使用 `resolve("/")[0]`。
1378
+ - **覆盖**:[src/test/unit/runtime-guard-windows.test.ts](../src/test/unit/runtime-guard-windows.test.ts) 覆盖 Windows 大小写和运行时关键子目录阻断。
1379
+
1380
+ #### ✅ 11.1.2 `changeCapture.ts` 的空设备路径 — 已修复
1381
+
1382
+ - **现状**:[src/runner/changeCapture.ts](../src/runner/changeCapture.ts) 使用平台辅助常量 `nullDevice`,Windows 为 `NUL`,其他平台为 `/dev/null`。
1383
+ - **覆盖**:[src/test/unit/change-capture-null-device.test.ts](../src/test/unit/change-capture-null-device.test.ts) 验证平台映射。
1384
+
1385
+ #### ✅ 11.1.3 Windows 快照路径大小写比较 — 已修复
1386
+
1387
+ - **现状**:[src/runner/changeCapture.ts](../src/runner/changeCapture.ts) 保留原始规范化路径用于证据展示,但在 Windows 下使用小写比较键处理 before/after 快照、tracked/ignored 集合、dirty path 和外部脏文件基线;POSIX 仍保持大小写敏感。
1388
+ - **覆盖**:[src/test/unit/runner/change-capture.test.ts](../src/test/unit/runner/change-capture.test.ts) 覆盖 Windows 大小写重命名/修改、大小写敏感目录碰撞、POSIX 重命名和外部基线匹配。
1389
+
1390
+ #### ✅ 11.1.4 Windows repo key 大小写匹配 — 已修复
1391
+
1392
+ - **现状**:[src/config.ts](../src/config.ts) 的存储 key 仍保留用户书写形式,但 `getRepoAllowedTestCommands` 与 `getRepoDirectAllowedCommands` 均通过 `comparablePath` 比较;Windows 下会转为小写绝对路径,因此 `MyRepo`/`myrepo` 不再导致 allowlist 失配。
1393
+
1394
+ #### ✅ 11.1.5 Windows Agent npm shim 无 shell 启动 — 已修复
1395
+
1396
+ - **现状**:[src/runner/agentInvocation.ts](../src/runner/agentInvocation.ts) 对 OpenCode 优先解析原生 `opencode.exe`;对 Codex、Claude、Gemini、Copilot、Qwen 等已知 npm adapter,验证受信 PATH 下的包名和 `package.json#bin` 后,使用受信 Node 可执行文件直接启动 CLI。未知 `.cmd`/`.bat`/`.ps1` wrapper 继续拒绝,不会回退到 shell。
1397
+ - **覆盖**:[src/test/unit/runner/agent-invocation.test.ts](../src/test/unit/runner/agent-invocation.test.ts) 覆盖原生 OpenCode、已知 npm shim、未知 wrapper 拒绝和 repo-local 同名 executable 排除。
1398
+
1399
+ #### ✅ 11.1.6 `postTaskCleanup.ts` 不再调用 `cmd rmdir` — 已修复
1400
+
1401
+ - **现状**:[src/runner/postTaskCleanup.ts](../src/runner/postTaskCleanup.ts) 使用 `fs.rmSync` 的有界重试;Windows 失败时只对目标树清除只读属性后再重试,不再启动 `cmd.exe /c rmdir`。Git 分类探测仍是同步且有界的本地命令,清理仅在任务收尾阶段执行。
1402
+
1403
+ ### 11.2 安全缺陷
1404
+
1405
+ #### 🟡 11.2.1 `pathGuard.ts` 的 TOCTOU 竞争窗口 — 开放(HIGH)
1406
+
1407
+ - **文件**:[src/security/pathGuard.ts](../src/security/pathGuard.ts)
1408
+ - **问题**:`realCandidate` 解析时刻与后续文件操作(read/write)之间存在 TOCTOU 窗口;攻击者可在校验通过后用符号链接替换目标路径逃逸工作区。
1409
+ - **影响**:在多用户/共享环境下可被利用绕过工作区限制。
1410
+ - **修复建议**:使用 `fs.open()` + `fstat()` + `O_NOFOLLOW`(POSIX)或 `FILE_FLAG_BACKUP_SEMANTICS`(Windows)确保文件描述符不被替换。
1411
+
1412
+ #### ✅ 11.2.2 `sensitiveGuard.ts` token/credentials 误匹配 — 已修复
1413
+
1414
+ - **现状**:[src/security/sensitiveGuard.ts](../src/security/sensitiveGuard.ts) 已把 `credentials`、`token` 收紧为完整 basename、受限扩展名或明确的 token-store/API-key 形态;`tokenizer.ts`、`credentials-handler.ts` 不再命中。NTFS ADS、NUL 和伪 `.patchwarden` 前缀也会被拒绝。
1415
+ - **保守策略**:basename 恰为 `config.json` 仍被阻止,这是对常见本地凭据文件的有意保守策略,不再归因于子串正则缺陷。
1416
+
1417
+ #### ℹ️ 11.2.3 `commandGuard.ts` 元字符校验 — 非缺陷
1418
+
1419
+ - `\x00-\x1F` 已包含换行、回车和制表符;配置命令 basename 还必须匹配 `[a-zA-Z0-9._-]+`。任务子进程不启用 shell,且实际可执行文件会从移除 repo cwd 的受信 PATH 解析,因此 `*`/`?` 不会获得 shell 通配语义。
1420
+
1421
+ #### ℹ️ 11.2.4 验证命令保持精确匹配 — 非缺陷
1422
+
1423
+ - `guardTestCommand`/`guardDirectCommand` 仅做首尾空白清理后与本地 allowlist 精确比较。`npm.cmd` 与 `NPM.CMD` 不自动等价是 fail-closed 设计,避免模型通过"近似命令"扩大授权;Windows npm/npx/pnpm 的实际无 shell 解析由进程安全层处理。
1424
+
1425
+ #### ✅ 11.2.5 `contentRedaction.ts` 常见 token 格式覆盖 — 已修复
1426
+
1427
+ - **现状**:[src/security/contentRedaction.ts](../src/security/contentRedaction.ts) 已覆盖 GitLab `glpat-`、Slack `xox*`、AWS access key、Google API key 和 JWT 形态。
1428
+ - **覆盖**:[src/test/unit/security/content-redaction.test.ts](../src/test/unit/security/content-redaction.test.ts) 使用合成凭据形态验证脱敏与普通文本不误报。
1429
+
1430
+ #### ✅ 11.2.6 `directPatch.ts` 原子替换 — 已修复
1431
+
1432
+ - **现状**:[src/direct/directPatch.ts](../src/direct/directPatch.ts) 在目标文件同目录写入随机临时文件后 `renameSync` 替换,并保留原文件权限;失败时清理临时文件。
1433
+ - **覆盖**:[src/test/unit/workspace/apply-patch.test.ts](../src/test/unit/workspace/apply-patch.test.ts) 检查补丁结果及成功后无残留临时文件。
1434
+
1435
+ #### ✅ 11.2.7 `discoveryTokenStore.ts` 有界存储 — 已修复
1436
+
1437
+ - **现状**:[src/security/discoveryTokenStore.ts](../src/security/discoveryTokenStore.ts) 在签发时清理过期记录,并将活动 token 数限制为 1024;达到上限时淘汰最旧记录。
1438
+ - **覆盖**:[src/test/unit/discovery/discovery-token-store.test.ts](../src/test/unit/discovery/discovery-token-store.test.ts) 覆盖未消费过期 token 清理和容量上限。
1439
+
1440
+ #### ✅ 11.2.8 Control token 常量时间比较 — 已修复
1441
+
1442
+ - **现状**:[src/control/middleware/auth.ts](../src/control/middleware/auth.ts) 将 header 与 control token 转为 Buffer,先比较长度,再使用 `crypto.timingSafeEqual`。
1443
+
1444
+ ### 11.3 错误处理缺陷
1445
+
1446
+ #### ℹ️ 11.3.1 `release_prepare`/`release_cleanup` 的同步调用 — 非缺陷
1447
+
1448
+ - **结论**:[src/tools/release/releaseMode.ts](../src/tools/release/releaseMode.ts) 中 `releasePrepare` 与 `releaseCleanup` 均为同步函数并直接返回 `ReleaseModeResult`,dispatch 不需要 `await`;`releaseCheck` 与 `releaseVerify` 才是异步函数。
1449
+
1450
+ #### ✅ 11.3.2 `watch.ts` 报告损坏的任务状态 — 已修复
1451
+
1452
+ - **现状**:[src/runner/watch.ts](../src/runner/watch.ts) 对非对象或无法解析的 `status.json` 记录带 `task_id` 和错误摘要的 warning 后跳过,不再静默吞掉。
1453
+
1454
+ #### ✅ 11.3.3 Watcher 与任务领取并发保护 — 已修复
1455
+
1456
+ - **现状**:[src/runner/watch.ts](../src/runner/watch.ts) 以排他创建的 watcher lock 拒绝活动重复实例,支持 stale PID 接管且只允许 owner 释放;tick 通过 non-overlapping runner 串行化。
1457
+ - **任务级保护**:[src/runner/taskStatusStore.ts](../src/runner/taskStatusStore.ts) 以同目录硬链接锁和原子 JSON 替换实现 `pending -> running` 单次领取;`cancelTask` 与 runner 共享同一状态锁。
1458
+ - **覆盖**:[src/test/unit/watcher-lock.test.ts](../src/test/unit/watcher-lock.test.ts)、[src/test/unit/watcher-runtime.test.ts](../src/test/unit/watcher-runtime.test.ts)、[src/test/unit/runner/task-status-store.test.ts](../src/test/unit/runner/task-status-store.test.ts) 和 [src/test/unit/runner/run-task-claim.test.ts](../src/test/unit/runner/run-task-claim.test.ts) 覆盖重复实例、stale lock、多进程领取、重复 runner 与取消竞态。
1459
+
1460
+ #### ℹ️ 11.3.4 `watch.ts` 保持轮询定时器引用 — 非缺陷
1461
+
1462
+ - **结论**:Watcher 是常驻进程,轮询定时器必须保持引用,才能在空闲时继续存活;SIGINT/SIGTERM handler 会显式释放 owner lock 并退出。`watcher-runtime.test.ts` 已覆盖空闲 watcher 常驻行为。
1463
+
1464
+ #### ✅ 11.3.5 `simpleProcess.ts` 追加写入 — 已修复
1465
+
1466
+ - **现状**:[src/runner/simpleProcess.ts](../src/runner/simpleProcess.ts) 与 [src/runner/processSecurity.ts](../src/runner/processSecurity.ts) 使用 `SecureProcessLogCapture` 有界收集、写前脱敏并最终 `appendFileSync`,不再执行 read-modify-write;异步完成以 child `close` 为准,避免在 stdio 排空前返回。
1467
+
1468
+ #### 🟡 11.3.6 `logging.ts` fatal handler 立即退出 — 开放(LOW)
1469
+
1470
+ - **现状**:[src/logging.ts](../src/logging.ts) 在 `uncaughtException` 记录结构化错误后调用 `process.exit(1)`,避免进程在未知状态继续运行。Direct patch、session/status JSON 等关键状态已使用原子替换,因此旧文档所称"中断 directPatch 非原子写入"不再成立。
1471
+ - **残余风险**:尚未 flush 的异步遥测或第三方输出可能丢失;若未来增加异步日志 sink,应在 fatal 路径增加有界 flush。
1472
+
1473
+ #### 🟡 11.3.7 best-effort cleanup 仍有静默 catch — 开放(LOW)
1474
+
1475
+ - **现状**:锁临时文件、失败后的清理和证据日志写入仍有少量 best-effort catch;关键状态更新会显式报错,但低价值清理失败可能只通过后续存在性检查暴露。
1476
+ - **建议**:增加有界 debug 计数,避免把潜在磁盘/权限问题长期静默化。
1477
+
1478
+ ### 11.4 代码质量问题
1479
+
1480
+ #### ✅ 11.4.1 TaskEntry 重建逻辑复用 — 已修复
1481
+
1482
+ - **现状**:[src/control/runtime.ts](../src/control/runtime.ts) 导出 `reconstructTaskEntry`,`routes/tasks.ts` 与 `routes/taskActions.ts` 均调用该 helper;旧的 `VALID_ACCEPTANCE2`/`taskStatus2` 复制代码已删除。
1483
+
1484
+ #### 🟡 11.4.2 `runTask.ts` 仍承担较多职责 — 开放(MEDIUM)
1485
+
1486
+ - **现状**:[src/tools/registry.ts](../src/tools/registry.ts) 已缩减至约 170 行,定义、catalog 与领域 dispatch 均已拆分,旧"1412 行 registry"结论失效。[src/runner/runTask.ts](../src/runner/runTask.ts) 仍超过 1300 行,包含生命周期编排和 managed process 控制。
1487
+ - **建议**:继续把 task context、managed process 与 artifact collection 按稳定边界拆分,同时保持任务状态机和安全检查顺序不变。
1488
+
1489
+ #### ℹ️ 11.4.3 `dispatchMap` 为配置无关 handler map — 非缺陷
1490
+
1491
+ - **结论**:[src/tools/registry.ts](../src/tools/registry.ts) 的 `dispatchMap` 只组合领域 handler;profile/feature gate 由 `getToolDefs()` 和 handler 内运行时守卫决定。`registerTools` 有意冻结一次连接的 active tool list,避免同一 MCP 连接的 list/call 漂移;配置变化应通过重连生效。
1492
+
1493
+ #### 🟡 11.4.4 `any` 仍集中在测试夹具和少量 dispatch 转换 — 开放(MEDIUM,v1.6.1 部分收敛)
1494
+
1495
+ - **现状**:v1.6.1 引入 [src/tools/dispatch/validation.ts](../src/tools/dispatch/validation.ts),把 dispatch 层的输入从 `as any` 断言迁到 `asString`/`asNumber`/`asBoolean`/`asStringArray`/`asRecord` 类型守卫;Desktop IPC、JSON、model-discovery、tunnel-process 数据也改为从 `unknown` 校验。剩余显著使用主要集中在大型 smoke harness 与少量 fixture。
1496
+ - **建议**:优先为 MCP 输入和 smoke fixture 定义共享类型,逐步改为 `unknown` + 类型守卫;不要用一次性全局替换破坏现有测试意图。
1497
+
1498
+ #### 🟡 11.4.5 `coreDispatch.ts`/`directDispatch.ts`/`goalDispatch.ts`/`releaseDispatch.ts` 残余 `as any` — 开放(MEDIUM)
1499
+
1500
+ - **文件**:`src/tools/dispatch/*.ts` 多处
1501
+ - **问题**:`template as any`、`file as "stdout" | "stderr" | ...`、`target_stage as any`、`operations as any` 等类型断言仍存在,依赖底层工具函数自行校验。v1.6.1 的 `validation.ts` 仅覆盖了部分高频入口。
1502
+ - **修复建议**:在 dispatch 层做参数 schema 校验,或在底层加显式校验。
1503
+
1504
+ #### ✅ 11.4.6 共享稳定 JSON 实现 — 已修复
1505
+
1506
+ - **现状**:[src/utils/stableJson.ts](../src/utils/stableJson.ts) 提供唯一 `stableJsonStringify`;catalog 与 registry 均导入该实现。
1507
+
1508
+ #### 🟡 11.4.7 Control URL 解码策略仍未完全统一 — 开放(LOW)
1509
+
1510
+ - **现状**:多数路径参数通过 `control/shared.ts` helper 处理;`routeTable.ts` 的 repo path 与静态文件 middleware 仍分别实现严格 400 错误语义,不能直接用"失败后返回原文"的 helper 替换。
1511
+ - **建议**:若继续抽象,应显式区分 strict decode 与 fallback decode 两种策略。
1512
+
1513
+ #### ✅ 11.4.8 Task safe handler 冗余 catch — 已修复
1514
+
1515
+ - **现状**:[src/control/routes/tasks.ts](../src/control/routes/tasks.ts) 的四个 safe handler 各保留单层 try/catch 与统一 ID 校验,旧的嵌套死代码已删除。
1516
+
1517
+ ### 11.5 架构问题
1518
+
1519
+ #### 🟡 11.5.1 模块级可变状态过多 — 开放(MEDIUM)
1520
+
1521
+ - **位置**:
1522
+ - `src/security/discoveryTokenStore.ts`:进程内 token store
1523
+ - `src/config.ts`:`_config` 模块级单例
1524
+ - `src/runner/watch.ts`:`executedTasks` 与连续失败计数
1525
+ - `src/control/runtime.ts`:状态摘要缓存
1526
+ - `src/tools/catalog/toolCatalog.ts`:最近 catalog snapshot
1527
+ - **影响**:测试需显式 reset,且多实例语义依赖进程隔离。`dispatchMap` 已确认配置无关,不再作为热重载缺陷证据。
1528
+
1529
+ #### ✅ 11.5.2 Watcher 重复 tick 失败升级 — 已修复
1530
+
1531
+ - **现状**:[src/runner/watch.ts](../src/runner/watch.ts) 记录连续 tick 失败;达到 3 次后心跳写为 `degraded` 并带有截断的 `last_error`。[src/watcherStatus.ts](../src/watcherStatus.ts) 将 degraded 视为不可用,并向 pending task 返回 `queued_but_watcher_degraded`。
1532
+
1533
+ #### ✅ 11.5.3 Diagnostic dispatch 循环依赖 — 已修复
1534
+
1535
+ - **现状**:[src/tools/dispatch/diagnosticDispatch.ts](../src/tools/dispatch/diagnosticDispatch.ts) 导出 `buildDiagnosticHandlers(dispatchTool)`;registry 注入 audited dispatcher,不再由 diagnostic 模块反向 import registry。
1536
+
1537
+ ### 11.6 测试覆盖差距
1538
+
1539
+ #### 🟡 11.6.1 核心模块测试覆盖 — 部分修复(MEDIUM)
1540
+
1541
+ 已覆盖的定向测试见 [10.1 节](#101-测试体系)。剩余差距主要是 `runTask.ts` 全生命周期的细粒度单元覆盖和 `control/server.ts` HTTP 路由级覆盖;现有 lifecycle/control smoke 不能完全替代单元测试。
1542
+
1543
+ #### ℹ️ 11.6.2 工具级覆盖清单 — 旧清单失效
1544
+
1545
+ - 旧版按 `src/tools/*.ts` 平铺路径列出的"未覆盖文件"已因工具分域重排和新增测试失效。当前仍应关注的是跨工具生命周期、Control HTTP 边界和真实 CLI 组合覆盖,而不是维护容易漂移的静态文件名清单。
1546
+
1547
+ ### 11.7 文档漂移
1548
+
1549
+ #### ✅ 11.7.1 CODE_WIKI 关键元数据漂移 — 已修复(v1.6.1 复核)
1550
+
1551
+ | 字段 | 上一版文档声称 | 当前源码 |
1552
+ | --- | --- | --- |
1553
+ | 源码版本 | `v1.6.0` | `v1.6.1` |
1554
+ | Schema Epoch | `2026-07-19-v15` | `2026-07-19-v15`(保持) |
1555
+ | 主包 `@types/node` | `^26.1.0` | `^18.19.0`(与 Node.js 18 最低运行时对齐) |
1556
+ | `chatgpt_direct` 工具数 | 15 | 14 |
1557
+
1558
+ > 注:根目录 `README.md` 仍写"v1.6.0",需在下一次 README 维护时同步到 v1.6.1。
1559
+
1560
+ #### ✅ 11.7.2 v1.5.1/v1.6.0/v1.6.1 新功能导览 — 已修复
1561
+
1562
+ 本文现已覆盖 `src/tools/dispatch/` 拆分、`src/control/` 路由拆分、`routeTable.ts`、`dispatch/validation.ts`、`utils/lockedJsonFile.ts`、Desktop 子包、模型发现、Agent 适配器、`androidDoctor`、`releaseMode`、`schemaDriftCheck`、`toolUsageStats`、`syncFile`、`goalSubgoalTask`、`runDirectVerificationBundle` 等。
1563
+
1564
+ #### ✅ 11.7.3 README 与 Direct Profile 工具数 — 已修复
1565
+
1566
+ - `README.md`、`README.en.md` 与 [src/tools/catalog/toolCatalog.ts](../src/tools/catalog/toolCatalog.ts) 均为 14 个 `chatgpt_direct` 工具。
1567
+
1568
+ ### 11.8 配置/校验差距
1569
+
1570
+ #### ✅ 11.8.1 Direct 数值配置范围 — 已修复
1571
+
1572
+ - **现状**:[src/config.ts](../src/config.ts) 要求 `directSessionTtlSeconds` 为 60–86400 的整数,并要求 `directMaxPatchBytes`、`directMaxFileBytes` 为正整数。
1573
+
1574
+ #### ✅ 11.8.2 `control/routes/sessions.ts` sessionId 校验 — 已修复
1575
+
1576
+ - **现状**:[src/control/routes/sessions.ts](../src/control/routes/sessions.ts) 的 detail、safe summary、finalize、audit、hide 路由统一调用 `isValidDirectSessionId` 白名单校验。
1577
+
1578
+ #### ✅ 11.8.3 Workspace 父目录穿越检查 — 已修复
1579
+
1580
+ - **现状**:[src/control/routes/workspace.ts](../src/control/routes/workspace.ts) 只拒绝值恰为 `..` 的路径段,不再误拒 `release..candidate` 之类合法目录名;Control Center smoke 覆盖该合法路径。
1581
+
1582
+ ### 11.9 并发问题
1583
+
1584
+ #### ✅ 11.9.1 多 watcher 与任务重复领取(见 11.3.3) — 已修复
1585
+
1586
+ #### ✅ 11.9.2 Direct session 原子 mutation — 已修复
1587
+
1588
+ - **现状**:[src/direct/directSessionStore.ts](../src/direct/directSessionStore.ts) 的 update、operation append 与 verification append 均通过 `mutateLockedJsonFileSync` 串行化,并以原子 JSON 替换提交;session identity 与记录结构在锁内重新校验。
1589
+
1590
+ #### ✅ 11.9.3 Control event append/trim 竞争 — 已修复
1591
+
1592
+ - **现状**:[src/control/runtime.ts](../src/control/runtime.ts) 在 `withFileLockSync(controlCenterEventsPath, ...)` 内顺序执行 append、大小检查和 trim;裁剪结果再通过原子替换提交。并发 writer 使用同一锁文件,不再能在读取与 rename 之间插入一条随后丢失的事件。
1593
+
1594
+ #### ✅ 11.9.4 Hidden ID 文件类型校验 — 已修复
1595
+
1596
+ - **现状**:[src/control/runtime.ts](../src/control/runtime.ts) 通过 `readStoredStringArray` 校验 `Array.isArray`,过滤非字符串/空字符串并去重;写入使用原子 JSON 替换。
1597
+
1598
+ #### ✅ 11.9.5 Goal mutation 与 worktree lifecycle 串行化 — 已修复(v1.6.1)
1599
+
1600
+ - **现状**:[src/goal/goalStore.ts](../src/goal/goalStore.ts) 与 [src/goal/worktreeManager.ts](../src/goal/worktreeManager.ts) 共享仓库级 lifecycle lock;create/merge/discard 串行化,跨进程孤儿锁由后续操作自动接管。
1601
+
1602
+ ### 11.10 依赖卫生
1603
+
1604
+ #### ℹ️ 11.10.1 主包保持单一直接运行时依赖 — 非缺陷
1605
+
1606
+ 主仓 `package.json` 的直接运行时依赖只有 `@modelcontextprotocol/sdk`(`^1.29.0`)。这不是"零运行时依赖";准确说法是"单一直接运行时依赖"。SDK 的传递依赖仍由 lockfile 管理。
1607
+
1608
+ #### ✅ 11.10.2 Node 类型与运行时基线对齐 — 已修复
1609
+
1610
+ - **现状**:主包使用 `@types/node ^18.19.0` 对齐 `engines.node >=18.0.0`;Electron 桌面子包独立使用 `@types/node ^24.0.0` 对齐其嵌入的 Node 运行时。
1611
+
1612
+ ### 11.11 优先级修复建议汇总
1613
+
1614
+ **已修复并从开放待办移除**:11.1.1–11.1.6、11.2.2、11.2.5–11.2.8、11.3.2、11.3.3、11.3.5、11.4.1、11.4.6、11.4.8、11.5.2、11.5.3、11.7.1–11.7.3、11.8.1–11.8.3、11.9.1–11.9.5、11.10.2。11.2.3、11.2.4、11.3.1、11.3.4、11.4.3、11.6.2、11.10.1 经复核不构成缺陷。
1615
+
1616
+ **仍需优先处理(HIGH,影响安全或核心功能)**:
1617
+
1618
+ 1. [11.2.1] `pathGuard.ts` 的 TOCTOU → 使用文件描述符级校验或等价的无跟随链接策略
1619
+
1620
+ **短期修复(MEDIUM,提升可靠性)**:
1621
+
1622
+ 2. [11.4.2] 继续按稳定边界拆分 `runTask.ts`
1623
+ 3. [11.4.4] 持续把剩余 MCP 输入与 smoke fixture 的 `any` 迁移为共享类型/类型守卫
1624
+ 4. [11.4.5] dispatch 层残余 `as any` 收敛
1625
+ 5. [11.6.1] 补齐 `runTask.ts` 生命周期与 Control HTTP 路由级单元测试
1626
+ 6. [11.5.1] 模块级可变状态逐步收敛(带 reset helper 或显式 lifecycle)
1627
+
1628
+ **长期改进(LOW,可维护性)**:
1629
+
1630
+ 7. [11.3.6] fatal handler 增加有界 flush,避免异步遥测丢失
1631
+ 8. [11.3.7] best-effort cleanup 增加有界 debug 计数
1632
+ 9. [11.4.7] 统一 strict/fallback 两类 URL 解码策略
1633
+
1634
+ ---
1635
+
1636
+ ## 附录:关键设计原则
1637
+
1638
+ 1. **最小权限**:MCP 工具不提供通用 Shell,每个工具只做一件事
1639
+ 2. **纵深防御**:多层守卫串联,任一层失效不导致整体失守
1640
+ 3. **不可信输入**:模型指令始终被视为不可信,本地配置才是信任源
1641
+ 4. **可审计性**:所有任务产出结构化证据,支持独立验收
1642
+ 5. **不自动破坏**:不自动 commit/push/publish/tag/release,需人工决策
1643
+ 6. **有界输出**:`safe_*` 系列工具返回有界摘要,避免触发平台内容过滤
1644
+ 7. **依赖最小化**:主仓保持单一直接运行时依赖(MCP SDK),其他核心功能优先使用 Node.js 内置模块
1645
+ 8. **跨平台**:Windows 与 POSIX 兼容(进程管理、路径处理、命令包装),平台特定回归集中记录在 11.1
1646
+ 9. **薄壳桌面**:所有业务逻辑在主仓 `dist/`,桌面只负责安装/检测/拉起/托盘/tunnel 下发
1647
+ 10. **配置即信任源**:Agent 命令、测试白名单、敏感路径模式都从本地配置读取,不从模型输入
1648
+ 11. **跨进程串行化(v1.6.1)**:所有持久化状态变更通过 `withFileLockSync` + `mutateLockedJsonFileSync` + 原子 `.tmp-{pid}` rename,保证 crash 后磁盘状态非旧即新