patchwarden 1.5.1 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (591) hide show
  1. package/PatchWarden.cmd +32 -2
  2. package/README.en.md +158 -26
  3. package/README.md +124 -24
  4. package/dist/assessments/agentAssessor.js +21 -12
  5. package/dist/assessments/assessmentDiagnostics.d.ts +29 -0
  6. package/dist/assessments/assessmentDiagnostics.js +132 -0
  7. package/dist/assessments/assessmentStore.d.ts +39 -0
  8. package/dist/assessments/assessmentStore.js +207 -45
  9. package/dist/assessments/securitySnapshot.d.ts +50 -0
  10. package/dist/assessments/securitySnapshot.js +158 -0
  11. package/dist/config.d.ts +15 -0
  12. package/dist/config.js +66 -13
  13. package/dist/control/fileManager.d.ts +2 -0
  14. package/dist/control/fileManager.js +39 -0
  15. package/dist/control/middleware/auth.d.ts +6 -0
  16. package/dist/control/middleware/auth.js +14 -5
  17. package/dist/control/middleware/static.js +16 -3
  18. package/dist/control/routeTable.d.ts +24 -0
  19. package/dist/control/routeTable.js +263 -0
  20. package/dist/control/routes/audit.js +17 -7
  21. package/dist/control/routes/evidence.js +1 -1
  22. package/dist/control/routes/lineage.js +1 -1
  23. package/dist/control/routes/process.d.ts +7 -0
  24. package/dist/control/routes/process.js +329 -42
  25. package/dist/control/routes/sessions.d.ts +9 -1
  26. package/dist/control/routes/sessions.js +84 -21
  27. package/dist/control/routes/status.d.ts +60 -1
  28. package/dist/control/routes/status.js +94 -15
  29. package/dist/control/routes/taskActions.js +24 -60
  30. package/dist/control/routes/tasks.d.ts +4 -0
  31. package/dist/control/routes/tasks.js +61 -62
  32. package/dist/control/routes/workspace.js +50 -23
  33. package/dist/control/runtime.d.ts +3 -2
  34. package/dist/control/runtime.js +65 -40
  35. package/dist/control/server.js +37 -365
  36. package/dist/control/shared.d.ts +25 -1
  37. package/dist/control/shared.js +64 -11
  38. package/dist/diagnostics/allowedTestCommandSafety.d.ts +1 -0
  39. package/dist/diagnostics/allowedTestCommandSafety.js +11 -0
  40. package/dist/direct/directAudit.js +234 -225
  41. package/dist/direct/directGuards.d.ts +2 -1
  42. package/dist/direct/directGuards.js +42 -29
  43. package/dist/direct/directPatch.d.ts +6 -1
  44. package/dist/direct/directPatch.js +55 -8
  45. package/dist/direct/directSessionStore.d.ts +16 -6
  46. package/dist/direct/directSessionStore.js +128 -35
  47. package/dist/direct/directVerification.js +8 -16
  48. package/dist/doctor.js +65 -57
  49. package/dist/goal/acceptanceEngine.d.ts +1 -1
  50. package/dist/goal/goalProgress.js +75 -69
  51. package/dist/goal/goalReport.js +5 -12
  52. package/dist/goal/goalStatus.d.ts +2 -0
  53. package/dist/goal/goalStatus.js +10 -3
  54. package/dist/goal/goalStore.d.ts +9 -2
  55. package/dist/goal/goalStore.js +152 -45
  56. package/dist/goal/handoffExport.js +6 -6
  57. package/dist/goal/specKitImport.js +97 -96
  58. package/dist/goal/subgoalSync.js +11 -9
  59. package/dist/goal/worktreeManager.d.ts +2 -0
  60. package/dist/goal/worktreeManager.js +209 -136
  61. package/dist/httpServer.js +111 -32
  62. package/dist/logging.js +5 -3
  63. package/dist/policy/projectPolicy.js +5 -2
  64. package/dist/release/releaseGate.d.ts +1 -1
  65. package/dist/release/releaseGate.js +78 -25
  66. package/dist/runner/agentInvocation.d.ts +14 -0
  67. package/dist/runner/agentInvocation.js +96 -4
  68. package/dist/runner/changeCapture.d.ts +13 -5
  69. package/dist/runner/changeCapture.js +243 -61
  70. package/dist/runner/postTaskCleanup.js +48 -19
  71. package/dist/runner/processSecurity.d.ts +56 -0
  72. package/dist/runner/processSecurity.js +373 -0
  73. package/dist/runner/runTask.d.ts +2 -1
  74. package/dist/runner/runTask.js +162 -93
  75. package/dist/runner/simpleProcess.d.ts +3 -0
  76. package/dist/runner/simpleProcess.js +50 -36
  77. package/dist/{taskProgress.d.ts → runner/taskProgress.d.ts} +1 -1
  78. package/dist/{taskProgress.js → runner/taskProgress.js} +2 -2
  79. package/dist/{taskRuntime.d.ts → runner/taskRuntime.d.ts} +3 -1
  80. package/dist/{taskRuntime.js → runner/taskRuntime.js} +3 -2
  81. package/dist/runner/taskStatusStore.d.ts +11 -0
  82. package/dist/runner/taskStatusStore.js +35 -0
  83. package/dist/runner/watch.d.ts +14 -1
  84. package/dist/runner/watch.js +320 -49
  85. package/dist/security/contentRedaction.js +2 -2
  86. package/dist/security/discoveryTokenStore.d.ts +3 -2
  87. package/dist/security/discoveryTokenStore.js +17 -2
  88. package/dist/security/loopbackHost.d.ts +2 -0
  89. package/dist/security/loopbackHost.js +16 -0
  90. package/dist/security/pathGuard.js +32 -17
  91. package/dist/security/planGuard.js +2 -1
  92. package/dist/security/runtimeGuard.js +9 -6
  93. package/dist/security/secretComparison.d.ts +3 -0
  94. package/dist/security/secretComparison.js +11 -0
  95. package/dist/security/sensitiveGuard.d.ts +5 -0
  96. package/dist/security/sensitiveGuard.js +27 -8
  97. package/dist/security/toolInvocationGuard.d.ts +2 -2
  98. package/dist/security/toolInvocationGuard.js +1 -1
  99. package/dist/security/workspaceRootGuard.d.ts +8 -0
  100. package/dist/security/workspaceRootGuard.js +47 -0
  101. package/dist/smoke-test.js +27 -26
  102. package/dist/tools/catalog/index.d.ts +4 -0
  103. package/dist/tools/catalog/index.js +4 -0
  104. package/dist/tools/{toolCatalog.js → catalog/toolCatalog.js} +3 -12
  105. package/dist/tools/{toolRegistry.d.ts → catalog/toolRegistry.d.ts} +3 -3
  106. package/dist/tools/{toolRegistry.js → catalog/toolRegistry.js} +6 -15
  107. package/dist/tools/definitions/toolDefs.d.ts +18 -0
  108. package/dist/tools/definitions/toolDefs.js +1177 -0
  109. package/dist/tools/{auditSession.d.ts → diagnostics/auditSession.d.ts} +1 -1
  110. package/dist/tools/{auditSession.js → diagnostics/auditSession.js} +2 -2
  111. package/dist/tools/{auditTask.d.ts → diagnostics/auditTask.d.ts} +2 -1
  112. package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +134 -36
  113. package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +18 -6
  114. package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +36 -8
  115. package/dist/tools/diagnostics/index.d.ts +6 -0
  116. package/dist/tools/diagnostics/index.js +6 -0
  117. package/dist/tools/{safeStatus.d.ts → diagnostics/safeStatus.d.ts} +3 -3
  118. package/dist/tools/{safeStatus.js → diagnostics/safeStatus.js} +6 -6
  119. package/dist/tools/{safeViews.d.ts → diagnostics/safeViews.d.ts} +12 -8
  120. package/dist/tools/{safeViews.js → diagnostics/safeViews.js} +23 -13
  121. package/dist/tools/{schemaDriftCheck.d.ts → diagnostics/schemaDriftCheck.d.ts} +1 -1
  122. package/dist/tools/{schemaDriftCheck.js → diagnostics/schemaDriftCheck.js} +2 -2
  123. package/dist/tools/{createDirectSession.js → direct/createDirectSession.js} +6 -6
  124. package/dist/tools/{finalizeDirectSession.d.ts → direct/finalizeDirectSession.d.ts} +1 -1
  125. package/dist/tools/direct/finalizeDirectSession.js +86 -0
  126. package/dist/tools/direct/index.d.ts +3 -0
  127. package/dist/tools/direct/index.js +3 -0
  128. package/dist/tools/{runDirectVerificationBundle.js → direct/runDirectVerificationBundle.js} +2 -2
  129. package/dist/tools/{discoverTools.d.ts → discovery/discoverTools.d.ts} +2 -2
  130. package/dist/tools/{discoverTools.js → discovery/discoverTools.js} +5 -5
  131. package/dist/tools/{explainTool.d.ts → discovery/explainTool.d.ts} +2 -2
  132. package/dist/tools/{explainTool.js → discovery/explainTool.js} +2 -2
  133. package/dist/tools/discovery/index.d.ts +3 -0
  134. package/dist/tools/discovery/index.js +3 -0
  135. package/dist/tools/{invokeDiscoveredTool.d.ts → discovery/invokeDiscoveredTool.d.ts} +2 -2
  136. package/dist/tools/{invokeDiscoveredTool.js → discovery/invokeDiscoveredTool.js} +5 -5
  137. package/dist/tools/dispatch/coreDispatch.js +32 -31
  138. package/dist/tools/dispatch/diagnosticDispatch.d.ts +3 -5
  139. package/dist/tools/dispatch/diagnosticDispatch.js +70 -68
  140. package/dist/tools/dispatch/directDispatch.js +11 -10
  141. package/dist/tools/dispatch/goalDispatch.js +5 -4
  142. package/dist/tools/dispatch/releaseDispatch.js +3 -2
  143. package/dist/tools/dispatch/validation.d.ts +7 -0
  144. package/dist/tools/dispatch/validation.js +71 -0
  145. package/dist/tools/{getPlan.js → goals/getPlan.js} +4 -4
  146. package/dist/tools/{goalSubgoalTask.d.ts → goals/goalSubgoalTask.d.ts} +1 -1
  147. package/dist/tools/goals/goalSubgoalTask.js +118 -0
  148. package/dist/tools/goals/index.d.ts +3 -0
  149. package/dist/tools/goals/index.js +3 -0
  150. package/dist/tools/{savePlan.js → goals/savePlan.js} +28 -8
  151. package/dist/tools/registry.d.ts +7 -11
  152. package/dist/tools/registry.js +17 -1178
  153. package/dist/tools/{checkReleaseGate.d.ts → release/checkReleaseGate.d.ts} +1 -1
  154. package/dist/tools/{checkReleaseGate.js → release/checkReleaseGate.js} +3 -3
  155. package/dist/tools/release/index.d.ts +2 -0
  156. package/dist/tools/release/index.js +2 -0
  157. package/dist/tools/{releaseMode.d.ts → release/releaseMode.d.ts} +2 -2
  158. package/dist/tools/{releaseMode.js → release/releaseMode.js} +46 -26
  159. package/dist/tools/tasks/cancelTask.d.ts +11 -0
  160. package/dist/tools/tasks/cancelTask.js +86 -0
  161. package/dist/tools/{createTask.d.ts → tasks/createTask.d.ts} +12 -4
  162. package/dist/tools/{createTask.js → tasks/createTask.js} +110 -34
  163. package/dist/tools/{diagnoseTask.d.ts → tasks/diagnoseTask.d.ts} +1 -1
  164. package/dist/tools/{diagnoseTask.js → tasks/diagnoseTask.js} +12 -16
  165. package/dist/tools/{evidencePack.js → tasks/evidencePack.js} +34 -22
  166. package/dist/tools/{getTaskFile.js → tasks/getTaskFile.js} +4 -4
  167. package/dist/tools/{getTaskStatus.d.ts → tasks/getTaskStatus.d.ts} +6 -1
  168. package/dist/tools/{getTaskStatus.js → tasks/getTaskStatus.js} +11 -6
  169. package/dist/tools/{getTaskStdoutTail.js → tasks/getTaskStdoutTail.js} +13 -11
  170. package/dist/tools/{getTaskSummary.js → tasks/getTaskSummary.js} +35 -36
  171. package/dist/tools/tasks/index.d.ts +18 -0
  172. package/dist/tools/tasks/index.js +18 -0
  173. package/dist/tools/tasks/killTask.d.ts +1 -0
  174. package/dist/tools/{listTasks.d.ts → tasks/listTasks.d.ts} +1 -1
  175. package/dist/tools/{listTasks.js → tasks/listTasks.js} +5 -4
  176. package/dist/tools/{reconcileTasks.d.ts → tasks/reconcileTasks.d.ts} +3 -2
  177. package/dist/tools/tasks/reconcileTasks.js +496 -0
  178. package/dist/tools/tasks/retryTask.js +60 -0
  179. package/dist/tools/{runTaskLoop.d.ts → tasks/runTaskLoop.d.ts} +6 -6
  180. package/dist/tools/{runTaskLoop.js → tasks/runTaskLoop.js} +63 -41
  181. package/dist/tools/tasks/runVerification.js +38 -0
  182. package/dist/tools/{taskLineage.js → tasks/taskLineage.js} +8 -7
  183. package/dist/tools/{taskOutputs.d.ts → tasks/taskOutputs.d.ts} +2 -2
  184. package/dist/tools/{taskOutputs.js → tasks/taskOutputs.js} +17 -16
  185. package/dist/tools/tasks/taskStates.d.ts +6 -0
  186. package/dist/tools/tasks/taskStates.js +52 -0
  187. package/dist/tools/{waitForTask.js → tasks/waitForTask.js} +3 -11
  188. package/dist/tools/{androidDoctor.js → workspace/androidDoctor.js} +16 -10
  189. package/dist/tools/{applyPatch.d.ts → workspace/applyPatch.d.ts} +1 -1
  190. package/dist/tools/workspace/applyPatch.js +53 -0
  191. package/dist/tools/{discardWorktree.js → workspace/discardWorktree.js} +3 -3
  192. package/dist/tools/workspace/index.d.ts +10 -0
  193. package/dist/tools/workspace/index.js +10 -0
  194. package/dist/tools/workspace/listAgents.d.ts +23 -0
  195. package/dist/tools/workspace/listAgents.js +56 -0
  196. package/dist/tools/{listWorkspace.js → workspace/listWorkspace.js} +3 -3
  197. package/dist/tools/{mergeWorktree.js → workspace/mergeWorktree.js} +3 -3
  198. package/dist/tools/{readWorkspaceFile.js → workspace/readWorkspaceFile.js} +8 -8
  199. package/dist/tools/{recommendAgentForTask.js → workspace/recommendAgentForTask.js} +3 -3
  200. package/dist/tools/{searchWorkspace.js → workspace/searchWorkspace.js} +5 -5
  201. package/dist/tools/{syncFile.d.ts → workspace/syncFile.d.ts} +1 -1
  202. package/dist/tools/workspace/syncFile.js +134 -0
  203. package/dist/utils/atomicFile.d.ts +7 -0
  204. package/dist/utils/atomicFile.js +67 -0
  205. package/dist/utils/boundedFile.d.ts +8 -0
  206. package/dist/utils/boundedFile.js +93 -0
  207. package/dist/utils/lockedJsonFile.d.ts +15 -0
  208. package/dist/utils/lockedJsonFile.js +224 -0
  209. package/dist/utils/platform.d.ts +11 -0
  210. package/dist/utils/platform.js +26 -0
  211. package/dist/utils/stableJson.d.ts +2 -0
  212. package/dist/utils/stableJson.js +12 -0
  213. package/dist/version.d.ts +3 -2
  214. package/dist/version.js +3 -2
  215. package/dist/watcherStatus.d.ts +2 -2
  216. package/dist/watcherStatus.js +18 -0
  217. package/docs/CODE_WIKI.md +808 -143
  218. package/docs/architecture.md +15 -55
  219. package/docs/assets/patchwarden-desktop-onboarding.png +0 -0
  220. package/docs/control-center/README.md +4 -4
  221. package/docs/dashboard-overview.md +4 -4
  222. package/docs/desktop-app.md +175 -0
  223. package/docs/evidence-pack-schema.md +3 -3
  224. package/docs/open-source-application.md +16 -11
  225. package/docs/release-checklist.md +1 -1
  226. package/docs/release-evidence.md +35 -39
  227. package/docs/task-safe-review-workflow.md +5 -5
  228. package/docs/threat-model.md +19 -1
  229. package/examples/config.example.json +4 -2
  230. package/package.json +27 -10
  231. package/scripts/README.md +15 -6
  232. package/scripts/build.js +11 -0
  233. package/scripts/checks/brand-check.js +3 -0
  234. package/scripts/checks/build-output-check.js +37 -0
  235. package/scripts/checks/control-center-smoke.js +102 -10
  236. package/scripts/checks/control-smoke.js +19 -8
  237. package/scripts/checks/http-mcp-smoke.js +82 -4
  238. package/scripts/checks/lifecycle-smoke.js +15 -12
  239. package/scripts/checks/mcp-manifest-check.js +1 -1
  240. package/scripts/checks/mcp-smoke.js +58 -4
  241. package/scripts/checks/package-manifest-check.js +20 -7
  242. package/scripts/checks/unit-tests.js +3 -5
  243. package/scripts/checks/watcher-supervisor-smoke.js +38 -1
  244. package/scripts/control/get-patchwarden-health.ps1 +1 -1
  245. package/scripts/control/manage-patchwarden.ps1 +69 -4
  246. package/scripts/control/provision-patchwarden-tunnel.ps1 +127 -0
  247. package/scripts/control/run-background-supervisor.ps1 +41 -0
  248. package/scripts/control/start-patchwarden-tunnel.ps1 +191 -33
  249. package/scripts/e2e/demo-runtime.mjs +153 -0
  250. package/{PatchWarden-Control-Tray.cmd → scripts/launchers/PatchWarden-Control-Tray.cmd} +3 -3
  251. package/scripts/launchers/PatchWarden-Control.cmd +6 -0
  252. package/{PatchWarden-Desktop.cmd → scripts/launchers/PatchWarden-Desktop.cmd} +2 -2
  253. package/scripts/launchers/Restart-PatchWarden-Control.cmd +6 -0
  254. package/{Stop-PatchWarden.cmd → scripts/launchers/Stop-PatchWarden.cmd} +2 -2
  255. package/scripts/lib/clean-generated-output.js +11 -0
  256. package/scripts/lib/file-discovery.js +12 -0
  257. package/scripts/release/desktop-preflight.js +211 -0
  258. package/scripts/release/pack-clean.js +59 -14
  259. package/src/assessments/agentAssessor.ts +26 -12
  260. package/src/assessments/assessmentDiagnostics.ts +172 -0
  261. package/src/assessments/assessmentStore.ts +288 -51
  262. package/src/assessments/securitySnapshot.ts +231 -0
  263. package/src/config.ts +70 -13
  264. package/src/control/fileManager.ts +40 -0
  265. package/src/control/middleware/auth.ts +22 -4
  266. package/src/control/middleware/static.ts +16 -3
  267. package/src/control/routeTable.ts +301 -0
  268. package/src/control/routes/audit.ts +14 -6
  269. package/src/control/routes/evidence.ts +1 -1
  270. package/src/control/routes/lineage.ts +1 -1
  271. package/src/control/routes/process.ts +341 -40
  272. package/src/control/routes/sessions.ts +88 -25
  273. package/src/control/routes/status.ts +134 -17
  274. package/src/control/routes/taskActions.ts +24 -58
  275. package/src/control/routes/tasks.ts +66 -58
  276. package/src/control/routes/workspace.ts +56 -25
  277. package/src/control/runtime.ts +71 -43
  278. package/src/control/server.ts +37 -342
  279. package/src/control/shared.ts +77 -11
  280. package/src/diagnostics/allowedTestCommandSafety.ts +12 -0
  281. package/src/direct/directAudit.ts +17 -9
  282. package/src/direct/directGuards.ts +72 -44
  283. package/src/direct/directPatch.ts +95 -8
  284. package/src/direct/directSessionStore.ts +213 -55
  285. package/src/direct/directVerification.ts +9 -23
  286. package/src/doctor.ts +76 -64
  287. package/src/goal/acceptanceEngine.ts +1 -1
  288. package/src/goal/goalProgress.ts +17 -13
  289. package/src/goal/goalReport.ts +5 -13
  290. package/src/goal/goalStatus.ts +19 -3
  291. package/src/goal/goalStore.ts +241 -54
  292. package/src/goal/handoffExport.ts +6 -6
  293. package/src/goal/specKitImport.ts +12 -12
  294. package/src/goal/subgoalSync.ts +11 -8
  295. package/src/goal/worktreeManager.ts +139 -22
  296. package/src/httpServer.ts +113 -30
  297. package/src/logging.ts +10 -3
  298. package/src/policy/projectPolicy.ts +5 -2
  299. package/src/release/releaseGate.ts +74 -27
  300. package/src/runner/agentInvocation.ts +126 -4
  301. package/src/runner/changeCapture.ts +291 -57
  302. package/src/runner/postTaskCleanup.ts +41 -17
  303. package/src/runner/processSecurity.ts +433 -0
  304. package/src/runner/runTask.ts +197 -97
  305. package/src/runner/simpleProcess.ts +66 -31
  306. package/src/{taskProgress.ts → runner/taskProgress.ts} +3 -3
  307. package/src/{taskRuntime.ts → runner/taskRuntime.ts} +6 -3
  308. package/src/runner/taskStatusStore.ts +69 -0
  309. package/src/runner/watch.ts +348 -52
  310. package/src/security/contentRedaction.ts +2 -2
  311. package/src/security/discoveryTokenStore.ts +18 -4
  312. package/src/security/loopbackHost.ts +19 -0
  313. package/src/security/pathGuard.ts +35 -16
  314. package/src/security/planGuard.ts +2 -1
  315. package/src/security/runtimeGuard.ts +9 -6
  316. package/src/security/secretComparison.ts +13 -0
  317. package/src/security/sensitiveGuard.ts +27 -8
  318. package/src/security/toolInvocationGuard.ts +2 -2
  319. package/src/security/workspaceRootGuard.ts +53 -0
  320. package/src/smoke-test.ts +27 -26
  321. package/src/tools/catalog/index.ts +4 -0
  322. package/src/tools/{toolCatalog.ts → catalog/toolCatalog.ts} +3 -12
  323. package/src/tools/{toolRegistry.ts → catalog/toolRegistry.ts} +7 -16
  324. package/src/tools/definitions/toolDefs.ts +1264 -0
  325. package/src/tools/{auditSession.ts → diagnostics/auditSession.ts} +2 -2
  326. package/src/tools/{auditTask.ts → diagnostics/auditTask.ts} +136 -39
  327. package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +37 -9
  328. package/src/tools/diagnostics/index.ts +6 -0
  329. package/src/tools/{safeStatus.ts → diagnostics/safeStatus.ts} +7 -7
  330. package/src/tools/{safeViews.ts → diagnostics/safeViews.ts} +27 -17
  331. package/src/tools/{schemaDriftCheck.ts → diagnostics/schemaDriftCheck.ts} +3 -3
  332. package/src/tools/{createDirectSession.ts → direct/createDirectSession.ts} +6 -6
  333. package/src/tools/{finalizeDirectSession.ts → direct/finalizeDirectSession.ts} +7 -5
  334. package/src/tools/direct/index.ts +3 -0
  335. package/src/tools/{runDirectVerificationBundle.ts → direct/runDirectVerificationBundle.ts} +2 -2
  336. package/src/tools/{discoverTools.ts → discovery/discoverTools.ts} +6 -6
  337. package/src/tools/{explainTool.ts → discovery/explainTool.ts} +3 -3
  338. package/src/tools/discovery/index.ts +3 -0
  339. package/src/tools/{invokeDiscoveredTool.ts → discovery/invokeDiscoveredTool.ts} +7 -7
  340. package/src/tools/dispatch/coreDispatch.ts +36 -31
  341. package/src/tools/dispatch/diagnosticDispatch.ts +26 -21
  342. package/src/tools/dispatch/directDispatch.ts +11 -10
  343. package/src/tools/dispatch/goalDispatch.ts +5 -4
  344. package/src/tools/dispatch/releaseDispatch.ts +3 -2
  345. package/src/tools/dispatch/validation.ts +78 -0
  346. package/src/tools/{getPlan.ts → goals/getPlan.ts} +4 -4
  347. package/src/tools/{goalSubgoalTask.ts → goals/goalSubgoalTask.ts} +41 -26
  348. package/src/tools/goals/index.ts +3 -0
  349. package/src/tools/{savePlan.ts → goals/savePlan.ts} +38 -8
  350. package/src/tools/registry.ts +17 -1261
  351. package/src/tools/{checkReleaseGate.ts → release/checkReleaseGate.ts} +3 -3
  352. package/src/tools/release/index.ts +2 -0
  353. package/src/tools/{releaseMode.ts → release/releaseMode.ts} +42 -24
  354. package/src/tools/tasks/cancelTask.ts +110 -0
  355. package/src/tools/{createTask.ts → tasks/createTask.ts} +161 -33
  356. package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +12 -16
  357. package/src/tools/{evidencePack.ts → tasks/evidencePack.ts} +36 -22
  358. package/src/tools/{getTaskFile.ts → tasks/getTaskFile.ts} +4 -4
  359. package/src/tools/{getTaskStatus.ts → tasks/getTaskStatus.ts} +13 -6
  360. package/src/tools/{getTaskStdoutTail.ts → tasks/getTaskStdoutTail.ts} +13 -11
  361. package/src/tools/{getTaskSummary.ts → tasks/getTaskSummary.ts} +46 -44
  362. package/src/tools/tasks/index.ts +18 -0
  363. package/src/tools/{listTasks.ts → tasks/listTasks.ts} +6 -5
  364. package/src/tools/{reconcileTasks.ts → tasks/reconcileTasks.ts} +228 -77
  365. package/src/tools/tasks/retryTask.ts +69 -0
  366. package/src/tools/{runTaskLoop.ts → tasks/runTaskLoop.ts} +73 -46
  367. package/src/tools/{runVerification.ts → tasks/runVerification.ts} +6 -3
  368. package/src/tools/{taskLineage.ts → tasks/taskLineage.ts} +8 -7
  369. package/src/tools/{taskOutputs.ts → tasks/taskOutputs.ts} +27 -20
  370. package/src/tools/tasks/taskStates.ts +54 -0
  371. package/src/tools/{waitForTask.ts → tasks/waitForTask.ts} +3 -12
  372. package/src/tools/{androidDoctor.ts → workspace/androidDoctor.ts} +15 -10
  373. package/src/tools/{applyPatch.ts → workspace/applyPatch.ts} +38 -16
  374. package/src/tools/{discardWorktree.ts → workspace/discardWorktree.ts} +3 -3
  375. package/src/tools/workspace/index.ts +10 -0
  376. package/src/tools/workspace/listAgents.ts +75 -0
  377. package/src/tools/{listWorkspace.ts → workspace/listWorkspace.ts} +3 -3
  378. package/src/tools/{mergeWorktree.ts → workspace/mergeWorktree.ts} +3 -3
  379. package/src/tools/{readWorkspaceFile.ts → workspace/readWorkspaceFile.ts} +8 -8
  380. package/src/tools/{recommendAgentForTask.ts → workspace/recommendAgentForTask.ts} +3 -3
  381. package/src/tools/{searchWorkspace.ts → workspace/searchWorkspace.ts} +5 -5
  382. package/src/tools/workspace/syncFile.ts +292 -0
  383. package/src/utils/atomicFile.ts +74 -0
  384. package/src/utils/boundedFile.ts +116 -0
  385. package/src/utils/lockedJsonFile.ts +251 -0
  386. package/src/utils/platform.ts +31 -0
  387. package/src/utils/stableJson.ts +12 -0
  388. package/src/version.ts +3 -2
  389. package/src/watcherStatus.ts +19 -1
  390. package/ui/desktop-bootstrap.js +11 -0
  391. package/ui/desktop-bridge.js +36 -0
  392. package/ui/desktop.css +160 -0
  393. package/ui/getting-started.js +93 -0
  394. package/ui/i18n.js +484 -0
  395. package/ui/log-parser.js +30 -0
  396. package/ui/pages/audit.html +12 -0
  397. package/ui/pages/dashboard.html +94 -147
  398. package/ui/pages/direct-sessions.html +123 -26
  399. package/ui/pages/getting-started.html +43 -0
  400. package/ui/pages/logs.html +134 -22
  401. package/ui/pages/settings.html +101 -0
  402. package/ui/pages/task-detail.html +12 -0
  403. package/ui/pages/tasks.html +54 -8
  404. package/ui/pages/workspace.html +38 -1
  405. package/ui/settings.js +254 -0
  406. package/PatchWarden-Control.cmd +0 -6
  407. package/Restart-PatchWarden-Control.cmd +0 -6
  408. package/dist/test/unit/acceptance-engine.test.d.ts +0 -1
  409. package/dist/test/unit/acceptance-engine.test.js +0 -228
  410. package/dist/test/unit/agent-router.test.d.ts +0 -1
  411. package/dist/test/unit/agent-router.test.js +0 -287
  412. package/dist/test/unit/android-doctor.test.d.ts +0 -1
  413. package/dist/test/unit/android-doctor.test.js +0 -118
  414. package/dist/test/unit/apply-patch.test.d.ts +0 -1
  415. package/dist/test/unit/apply-patch.test.js +0 -225
  416. package/dist/test/unit/audit-checks.test.d.ts +0 -1
  417. package/dist/test/unit/audit-checks.test.js +0 -350
  418. package/dist/test/unit/chinese-path.test.d.ts +0 -1
  419. package/dist/test/unit/chinese-path.test.js +0 -91
  420. package/dist/test/unit/command-guard.test.d.ts +0 -1
  421. package/dist/test/unit/command-guard.test.js +0 -160
  422. package/dist/test/unit/create-task.test.d.ts +0 -1
  423. package/dist/test/unit/create-task.test.js +0 -197
  424. package/dist/test/unit/diagnose-task.test.d.ts +0 -1
  425. package/dist/test/unit/diagnose-task.test.js +0 -457
  426. package/dist/test/unit/direct-guards.test.d.ts +0 -1
  427. package/dist/test/unit/direct-guards.test.js +0 -328
  428. package/dist/test/unit/discovery-token-store.test.d.ts +0 -1
  429. package/dist/test/unit/discovery-token-store.test.js +0 -139
  430. package/dist/test/unit/evidence-pack.test.d.ts +0 -1
  431. package/dist/test/unit/evidence-pack.test.js +0 -224
  432. package/dist/test/unit/get-task-status.test.d.ts +0 -1
  433. package/dist/test/unit/get-task-status.test.js +0 -174
  434. package/dist/test/unit/get-task-summary.test.d.ts +0 -1
  435. package/dist/test/unit/get-task-summary.test.js +0 -146
  436. package/dist/test/unit/goal-graph.test.d.ts +0 -1
  437. package/dist/test/unit/goal-graph.test.js +0 -298
  438. package/dist/test/unit/goal-progress.test.d.ts +0 -1
  439. package/dist/test/unit/goal-progress.test.js +0 -381
  440. package/dist/test/unit/goal-report.test.d.ts +0 -1
  441. package/dist/test/unit/goal-report.test.js +0 -159
  442. package/dist/test/unit/goal-status.test.d.ts +0 -1
  443. package/dist/test/unit/goal-status.test.js +0 -215
  444. package/dist/test/unit/goal-store.test.d.ts +0 -1
  445. package/dist/test/unit/goal-store.test.js +0 -253
  446. package/dist/test/unit/goal-subgoal-task.test.d.ts +0 -1
  447. package/dist/test/unit/goal-subgoal-task.test.js +0 -55
  448. package/dist/test/unit/goal-tools-registry.test.d.ts +0 -1
  449. package/dist/test/unit/goal-tools-registry.test.js +0 -192
  450. package/dist/test/unit/handoff-export.test.d.ts +0 -1
  451. package/dist/test/unit/handoff-export.test.js +0 -263
  452. package/dist/test/unit/invoke-discovered-tool.test.d.ts +0 -1
  453. package/dist/test/unit/invoke-discovered-tool.test.js +0 -167
  454. package/dist/test/unit/logging.test.d.ts +0 -1
  455. package/dist/test/unit/logging.test.js +0 -397
  456. package/dist/test/unit/path-guard.test.d.ts +0 -1
  457. package/dist/test/unit/path-guard.test.js +0 -133
  458. package/dist/test/unit/post-task-cleanup.test.d.ts +0 -1
  459. package/dist/test/unit/post-task-cleanup.test.js +0 -48
  460. package/dist/test/unit/project-policy-release-mode.test.d.ts +0 -1
  461. package/dist/test/unit/project-policy-release-mode.test.js +0 -125
  462. package/dist/test/unit/reconcile-tasks.test.d.ts +0 -1
  463. package/dist/test/unit/reconcile-tasks.test.js +0 -456
  464. package/dist/test/unit/release-gate.test.d.ts +0 -1
  465. package/dist/test/unit/release-gate.test.js +0 -242
  466. package/dist/test/unit/run-task-loop.test.d.ts +0 -1
  467. package/dist/test/unit/run-task-loop.test.js +0 -380
  468. package/dist/test/unit/safe-status.test.d.ts +0 -1
  469. package/dist/test/unit/safe-status.test.js +0 -165
  470. package/dist/test/unit/safe-views.test.d.ts +0 -1
  471. package/dist/test/unit/safe-views.test.js +0 -171
  472. package/dist/test/unit/schema-drift-check.test.d.ts +0 -1
  473. package/dist/test/unit/schema-drift-check.test.js +0 -176
  474. package/dist/test/unit/sensitive-guard.test.d.ts +0 -1
  475. package/dist/test/unit/sensitive-guard.test.js +0 -104
  476. package/dist/test/unit/spec-kit-import.test.d.ts +0 -1
  477. package/dist/test/unit/spec-kit-import.test.js +0 -341
  478. package/dist/test/unit/subgoal-sync.test.d.ts +0 -1
  479. package/dist/test/unit/subgoal-sync.test.js +0 -183
  480. package/dist/test/unit/sync-file.test.d.ts +0 -1
  481. package/dist/test/unit/sync-file.test.js +0 -154
  482. package/dist/test/unit/tool-invocation-guard.test.d.ts +0 -1
  483. package/dist/test/unit/tool-invocation-guard.test.js +0 -432
  484. package/dist/test/unit/tool-usage-stats.test.d.ts +0 -1
  485. package/dist/test/unit/tool-usage-stats.test.js +0 -300
  486. package/dist/test/unit/toolSearch.test.d.ts +0 -1
  487. package/dist/test/unit/toolSearch.test.js +0 -571
  488. package/dist/test/unit/wait-for-task.test.d.ts +0 -1
  489. package/dist/test/unit/wait-for-task.test.js +0 -144
  490. package/dist/test/unit/watcher-status.test.d.ts +0 -1
  491. package/dist/test/unit/watcher-status.test.js +0 -169
  492. package/dist/test/unit/worktree-manager.test.d.ts +0 -1
  493. package/dist/test/unit/worktree-manager.test.js +0 -176
  494. package/dist/tools/applyPatch.js +0 -41
  495. package/dist/tools/cancelTask.d.ts +0 -30
  496. package/dist/tools/cancelTask.js +0 -64
  497. package/dist/tools/finalizeDirectSession.js +0 -84
  498. package/dist/tools/goalSubgoalTask.js +0 -110
  499. package/dist/tools/killTask.d.ts +0 -15
  500. package/dist/tools/listAgents.d.ts +0 -12
  501. package/dist/tools/listAgents.js +0 -42
  502. package/dist/tools/reconcileTasks.js +0 -352
  503. package/dist/tools/retryTask.js +0 -45
  504. package/dist/tools/runVerification.js +0 -36
  505. package/dist/tools/syncFile.js +0 -65
  506. package/docs/control-center/control-center-daily-driver.md +0 -211
  507. package/docs/control-center/control-center-mvp.md +0 -205
  508. package/docs/control-center/control-center-phase2.md +0 -159
  509. package/docs/execution-plan-2026-07-09.md +0 -315
  510. package/docs/release-v0.6.0.md +0 -71
  511. package/docs/release-v0.6.1.md +0 -75
  512. package/docs/release-v0.6.4.md +0 -45
  513. package/docs/roadmap-execution-and-acceptance.md +0 -365
  514. package/src/test/unit/acceptance-engine.test.ts +0 -261
  515. package/src/test/unit/agent-router.test.ts +0 -342
  516. package/src/test/unit/android-doctor.test.ts +0 -158
  517. package/src/test/unit/apply-patch.test.ts +0 -293
  518. package/src/test/unit/audit-checks.test.ts +0 -567
  519. package/src/test/unit/chinese-path.test.ts +0 -106
  520. package/src/test/unit/command-guard.test.ts +0 -221
  521. package/src/test/unit/create-task.test.ts +0 -255
  522. package/src/test/unit/diagnose-task.test.ts +0 -544
  523. package/src/test/unit/direct-guards.test.ts +0 -467
  524. package/src/test/unit/discovery-token-store.test.ts +0 -181
  525. package/src/test/unit/evidence-pack.test.ts +0 -251
  526. package/src/test/unit/get-task-status.test.ts +0 -203
  527. package/src/test/unit/get-task-summary.test.ts +0 -173
  528. package/src/test/unit/goal-graph.test.ts +0 -347
  529. package/src/test/unit/goal-progress.test.ts +0 -538
  530. package/src/test/unit/goal-report.test.ts +0 -189
  531. package/src/test/unit/goal-status.test.ts +0 -270
  532. package/src/test/unit/goal-store.test.ts +0 -318
  533. package/src/test/unit/goal-subgoal-task.test.ts +0 -72
  534. package/src/test/unit/goal-tools-registry.test.ts +0 -245
  535. package/src/test/unit/handoff-export.test.ts +0 -295
  536. package/src/test/unit/invoke-discovered-tool.test.ts +0 -216
  537. package/src/test/unit/logging.test.ts +0 -497
  538. package/src/test/unit/path-guard.test.ts +0 -180
  539. package/src/test/unit/post-task-cleanup.test.ts +0 -53
  540. package/src/test/unit/project-policy-release-mode.test.ts +0 -156
  541. package/src/test/unit/reconcile-tasks.test.ts +0 -551
  542. package/src/test/unit/release-gate.test.ts +0 -314
  543. package/src/test/unit/run-task-loop.test.ts +0 -425
  544. package/src/test/unit/safe-status.test.ts +0 -187
  545. package/src/test/unit/safe-views.test.ts +0 -184
  546. package/src/test/unit/schema-drift-check.test.ts +0 -260
  547. package/src/test/unit/sensitive-guard.test.ts +0 -124
  548. package/src/test/unit/spec-kit-import.test.ts +0 -429
  549. package/src/test/unit/subgoal-sync.test.ts +0 -236
  550. package/src/test/unit/sync-file.test.ts +0 -231
  551. package/src/test/unit/tool-invocation-guard.test.ts +0 -542
  552. package/src/test/unit/tool-usage-stats.test.ts +0 -384
  553. package/src/test/unit/toolSearch.test.ts +0 -756
  554. package/src/test/unit/wait-for-task.test.ts +0 -176
  555. package/src/test/unit/watcher-status.test.ts +0 -190
  556. package/src/test/unit/worktree-manager.test.ts +0 -247
  557. package/src/tools/cancelTask.ts +0 -70
  558. package/src/tools/listAgents.ts +0 -54
  559. package/src/tools/retryTask.ts +0 -53
  560. package/src/tools/syncFile.ts +0 -122
  561. /package/dist/tools/{toolCatalog.d.ts → catalog/toolCatalog.d.ts} +0 -0
  562. /package/dist/tools/{toolSearch.d.ts → catalog/toolSearch.d.ts} +0 -0
  563. /package/dist/tools/{toolSearch.js → catalog/toolSearch.js} +0 -0
  564. /package/dist/tools/{toolUsageStats.d.ts → catalog/toolUsageStats.d.ts} +0 -0
  565. /package/dist/tools/{toolUsageStats.js → catalog/toolUsageStats.js} +0 -0
  566. /package/dist/tools/{createDirectSession.d.ts → direct/createDirectSession.d.ts} +0 -0
  567. /package/dist/tools/{runDirectVerificationBundle.d.ts → direct/runDirectVerificationBundle.d.ts} +0 -0
  568. /package/dist/tools/{getPlan.d.ts → goals/getPlan.d.ts} +0 -0
  569. /package/dist/tools/{savePlan.d.ts → goals/savePlan.d.ts} +0 -0
  570. /package/dist/tools/{evidencePack.d.ts → tasks/evidencePack.d.ts} +0 -0
  571. /package/dist/tools/{getTaskFile.d.ts → tasks/getTaskFile.d.ts} +0 -0
  572. /package/dist/tools/{getTaskProgress.d.ts → tasks/getTaskProgress.d.ts} +0 -0
  573. /package/dist/tools/{getTaskProgress.js → tasks/getTaskProgress.js} +0 -0
  574. /package/dist/tools/{getTaskStdoutTail.d.ts → tasks/getTaskStdoutTail.d.ts} +0 -0
  575. /package/dist/tools/{getTaskSummary.d.ts → tasks/getTaskSummary.d.ts} +0 -0
  576. /package/dist/tools/{killTask.js → tasks/killTask.js} +0 -0
  577. /package/dist/tools/{retryTask.d.ts → tasks/retryTask.d.ts} +0 -0
  578. /package/dist/tools/{runVerification.d.ts → tasks/runVerification.d.ts} +0 -0
  579. /package/dist/tools/{taskLineage.d.ts → tasks/taskLineage.d.ts} +0 -0
  580. /package/dist/tools/{waitForTask.d.ts → tasks/waitForTask.d.ts} +0 -0
  581. /package/dist/tools/{androidDoctor.d.ts → workspace/androidDoctor.d.ts} +0 -0
  582. /package/dist/tools/{discardWorktree.d.ts → workspace/discardWorktree.d.ts} +0 -0
  583. /package/dist/tools/{listWorkspace.d.ts → workspace/listWorkspace.d.ts} +0 -0
  584. /package/dist/tools/{mergeWorktree.d.ts → workspace/mergeWorktree.d.ts} +0 -0
  585. /package/dist/tools/{readWorkspaceFile.d.ts → workspace/readWorkspaceFile.d.ts} +0 -0
  586. /package/dist/tools/{recommendAgentForTask.d.ts → workspace/recommendAgentForTask.d.ts} +0 -0
  587. /package/dist/tools/{searchWorkspace.d.ts → workspace/searchWorkspace.d.ts} +0 -0
  588. /package/src/tools/{toolSearch.ts → catalog/toolSearch.ts} +0 -0
  589. /package/src/tools/{toolUsageStats.ts → catalog/toolUsageStats.ts} +0 -0
  590. /package/src/tools/{getTaskProgress.ts → tasks/getTaskProgress.ts} +0 -0
  591. /package/src/tools/{killTask.ts → tasks/killTask.ts} +0 -0
@@ -3,7 +3,10 @@
3
3
  [string]$Profile = "patchwarden",
4
4
  [ValidateSet("chatgpt_core", "chatgpt_direct")]
5
5
  [string]$ToolProfile = "chatgpt_core",
6
- [string]$ProxyUrl = $(if ($env:HTTPS_PROXY) { $env:HTTPS_PROXY } else { "http://127.0.0.1:7892" }),
6
+ [ValidateSet("", "environment", "none", "manual")]
7
+ [string]$ProxyMode = "",
8
+ [AllowEmptyString()]
9
+ [string]$ProxyUrl = "",
7
10
  [string]$TunnelClientExe = $(if ($env:TUNNEL_CLIENT_EXE) { $env:TUNNEL_CLIENT_EXE } else { $env:PATCHWARDEN_TUNNEL_CLIENT_EXE }),
8
11
  [string]$OpencodeBin = $env:OPENCODE_BIN_DIR,
9
12
  [string]$ConfigPath = $env:PATCHWARDEN_CONFIG,
@@ -22,6 +25,47 @@
22
25
 
23
26
  $ErrorActionPreference = "Stop"
24
27
 
28
+ if ([string]::IsNullOrWhiteSpace($ProxyMode)) {
29
+ $ProxyMode = if ($ToolProfile -eq "chatgpt_direct" -and $env:PATCHWARDEN_DIRECT_PROXY_MODE) {
30
+ $env:PATCHWARDEN_DIRECT_PROXY_MODE
31
+ } elseif ($env:PATCHWARDEN_TUNNEL_PROXY_MODE) {
32
+ $env:PATCHWARDEN_TUNNEL_PROXY_MODE
33
+ } else {
34
+ "environment"
35
+ }
36
+ }
37
+ if ([string]::IsNullOrWhiteSpace($ProxyUrl)) {
38
+ $ProxyUrl = if ($ToolProfile -eq "chatgpt_direct" -and $env:PATCHWARDEN_DIRECT_PROXY_URL) {
39
+ $env:PATCHWARDEN_DIRECT_PROXY_URL
40
+ } else {
41
+ $env:PATCHWARDEN_TUNNEL_PROXY_URL
42
+ }
43
+ }
44
+
45
+ $script:UseProxyArgument = $false
46
+ switch ($ProxyMode) {
47
+ "manual" {
48
+ if ([string]::IsNullOrWhiteSpace($ProxyUrl)) { throw "Manual proxy mode requires ProxyUrl." }
49
+ $proxyUri = $null
50
+ if (-not [Uri]::TryCreate($ProxyUrl, [UriKind]::Absolute, [ref]$proxyUri) -or $proxyUri.Scheme -notin @("http", "https", "socks5")) {
51
+ throw "ProxyUrl must use http, https, or socks5."
52
+ }
53
+ if (-not [string]::IsNullOrEmpty($proxyUri.UserInfo)) { throw "ProxyUrl must not contain credentials." }
54
+ $env:HTTP_PROXY = $ProxyUrl
55
+ $env:HTTPS_PROXY = $ProxyUrl
56
+ $env:ALL_PROXY = $ProxyUrl
57
+ $script:UseProxyArgument = $true
58
+ }
59
+ "none" {
60
+ Remove-Item Env:HTTP_PROXY, Env:HTTPS_PROXY, Env:ALL_PROXY -ErrorAction SilentlyContinue
61
+ }
62
+ "environment" {
63
+ $script:UseProxyArgument = -not [string]::IsNullOrWhiteSpace($env:HTTPS_PROXY)
64
+ }
65
+ default { throw "Unsupported proxy mode: $ProxyMode" }
66
+ }
67
+ $env:NO_PROXY = "localhost,127.0.0.1,::1"
68
+
25
69
  if ([string]::IsNullOrWhiteSpace($HealthListenAddr)) {
26
70
  if ($Profile -eq "patchwarden-direct" -or $ToolProfile -eq "chatgpt_direct") {
27
71
  $HealthListenAddr = "127.0.0.1:8081"
@@ -36,6 +80,15 @@ if (-not $ConfigPath) {
36
80
  }
37
81
  $McpLauncherName = if ($ToolProfile -eq "chatgpt_direct") { "patchwarden-mcp-direct.cmd" } else { "patchwarden-mcp-stdio.cmd" }
38
82
  $McpStdioLauncher = Join-Path $ProjectRoot "scripts\mcp\$McpLauncherName"
83
+ $McpEntryPath = Join-Path $ProjectRoot "dist\index.js"
84
+ $WatcherEntryPath = Join-Path $ProjectRoot "dist\runner\watch.js"
85
+ $RuntimeBuildLayout = if (Test-Path -LiteralPath (Join-Path $ProjectRoot ".git")) {
86
+ "git_worktree"
87
+ } elseif ($ProjectRoot -match '(?i)[\\/]resources[\\/]core$') {
88
+ "desktop_packaged_core"
89
+ } else {
90
+ "standalone_build"
91
+ }
39
92
  $McpStdioLauncherForTunnel = $McpStdioLauncher -replace "\\", "/"
40
93
  $OpencodeConfigHome = Join-Path $env:LOCALAPPDATA "patchwarden\opencode-config"
41
94
  $ProfilePath = Join-Path $env:APPDATA "tunnel-client\$Profile.yaml"
@@ -58,12 +111,35 @@ $script:WatcherManaged = $false
58
111
  $script:WatcherRestartAttempts = 0
59
112
  $script:WatcherHealthySince = $null
60
113
  $script:WatcherRestartExhausted = $false
61
- $DefaultTunnelClientPath = "D:\ai_agent\tunnel-client-v0.0.9--context-conduit-topaz-windows-amd64\tunnel-client.exe"
62
-
63
114
  if ($ToolProfile -eq "chatgpt_direct") {
64
115
  $SkipWatcher = $true
65
116
  }
66
117
 
118
+ $script:AgentEnvironmentNames = @(
119
+ ([string]$env:PATCHWARDEN_AGENT_ENV_ALLOWLIST -split ';') |
120
+ Where-Object { $_ -match '^[A-Za-z_][A-Za-z0-9_]*$' } |
121
+ Select-Object -Unique
122
+ )
123
+
124
+ function Suspend-AgentProviderEnvironment {
125
+ $saved = @{}
126
+ foreach ($name in $script:AgentEnvironmentNames) {
127
+ $item = Get-Item -LiteralPath "Env:$name" -ErrorAction SilentlyContinue
128
+ if ($null -ne $item) {
129
+ $saved[$name] = [string]$item.Value
130
+ Remove-Item -LiteralPath "Env:$name" -ErrorAction SilentlyContinue
131
+ }
132
+ }
133
+ return $saved
134
+ }
135
+
136
+ function Restore-AgentProviderEnvironment {
137
+ param([hashtable]$Saved)
138
+ foreach ($name in $Saved.Keys) {
139
+ Set-Item -LiteralPath "Env:$name" -Value $Saved[$name]
140
+ }
141
+ }
142
+
67
143
  function Assert-File {
68
144
  param([string]$Path, [string]$Name)
69
145
  if (-not (Test-Path -LiteralPath $Path)) {
@@ -176,6 +252,14 @@ function Get-LastDiagnosticLine {
176
252
  return $Fallback
177
253
  }
178
254
 
255
+ function Get-MaskedTunnelId {
256
+ param([string]$Value)
257
+ if ([string]::IsNullOrWhiteSpace($Value)) { return $null }
258
+ $trimmed = $Value.Trim()
259
+ if ($trimmed.Length -le 8) { return "***" }
260
+ return "$($trimmed.Substring(0, 4))***$($trimmed.Substring($trimmed.Length - 4))"
261
+ }
262
+
179
263
  function Write-TunnelStatus {
180
264
  param(
181
265
  [string]$Status,
@@ -196,6 +280,8 @@ function Write-TunnelStatus {
196
280
  status = $Status
197
281
  reason_code = $ReasonCode
198
282
  ready = $Ready
283
+ profile = $Profile
284
+ tunnel_id_masked = Get-MaskedTunnelId -Value $TunnelId
199
285
  attempt = $Attempt
200
286
  pid = $ProcessId
201
287
  checked_at = (Get-Date).ToUniversalTime().ToString("o")
@@ -214,6 +300,12 @@ function Write-TunnelStatus {
214
300
  tool_manifest_sha256 = if ($script:ToolManifest) { $script:ToolManifest.tool_manifest_sha256 } else { $null }
215
301
  tools_ready = [bool]($script:ToolManifest -and $script:ToolManifest.ok)
216
302
  core_tools_ready = [bool]($ToolProfile -eq "chatgpt_core" -and $script:ToolManifest -and $script:ToolManifest.ok)
303
+ project_root = $ProjectRoot
304
+ config_path = $ConfigPath
305
+ mcp_entry_path = $McpEntryPath
306
+ watcher_entry_path = $WatcherEntryPath
307
+ validator_module_path = (Join-Path $ProjectRoot "dist\assessments\assessmentStore.js")
308
+ runtime_build_layout = $RuntimeBuildLayout
217
309
  }
218
310
  $temporary = "$StatusFile.tmp"
219
311
  $payload | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath $temporary -Encoding UTF8
@@ -230,9 +322,17 @@ function Get-DiagnosticCode {
230
322
  }
231
323
 
232
324
  function Invoke-TunnelDoctor {
233
- $output = (& $TunnelClientExe doctor --profile $Profile --explain --json --http-proxy env:HTTPS_PROXY 2>&1 | Out-String)
325
+ $doctorArguments = @("doctor", "--profile", $Profile, "--explain", "--json")
326
+ if ($script:UseProxyArgument) { $doctorArguments += @("--http-proxy", "env:HTTPS_PROXY") }
327
+ $savedAgentEnvironment = Suspend-AgentProviderEnvironment
328
+ try {
329
+ $output = (& $TunnelClientExe @doctorArguments 2>&1 | Out-String)
330
+ $exitCode = $LASTEXITCODE
331
+ } finally {
332
+ Restore-AgentProviderEnvironment -Saved $savedAgentEnvironment
333
+ }
234
334
  return [pscustomobject]@{
235
- ExitCode = $LASTEXITCODE
335
+ ExitCode = $exitCode
236
336
  Output = $output
237
337
  ReasonCode = Get-DiagnosticCode $output
238
338
  }
@@ -269,6 +369,30 @@ function Get-WatcherHeartbeatState {
269
369
  }
270
370
  }
271
371
 
372
+ function Repair-StaleWatcherLock {
373
+ $settings = Get-WatcherSettings
374
+ $lockPath = Join-Path (Split-Path -Parent $settings.HeartbeatPath) "watcher.lock"
375
+ if (-not (Test-Path -LiteralPath $lockPath)) { return "not_present" }
376
+ try {
377
+ $lock = Get-Content -LiteralPath $lockPath -Raw -Encoding UTF8 | ConvertFrom-Json
378
+ $lockPid = [int]$lock.pid
379
+ if ($lockPid -le 0) { return "unverified" }
380
+ $candidate = Get-CimInstance Win32_Process -Filter "ProcessId = $lockPid" -ErrorAction Stop
381
+ if ($candidate) {
382
+ $normalizedCommand = ([string]$candidate.CommandLine).Replace("/", "\")
383
+ $normalizedExpected = ([System.IO.Path]::GetFullPath($WatcherEntryPath)).Replace("/", "\")
384
+ $isExpectedWatcher = [string]$candidate.Name -ieq "node.exe" -and
385
+ $normalizedCommand.IndexOf($normalizedExpected, [StringComparison]::OrdinalIgnoreCase) -ge 0
386
+ if ($isExpectedWatcher) { return "expected_watcher_alive" }
387
+ }
388
+ Remove-Item -LiteralPath $lockPath -Force -ErrorAction Stop
389
+ Write-Host "[watch] Removed stale watcher lock after PID identity verification."
390
+ return "removed"
391
+ } catch {
392
+ return "unverified"
393
+ }
394
+ }
395
+
272
396
  function Write-WatcherStatus {
273
397
  param([string]$Status, [string]$LastError = $null)
274
398
  New-Item -ItemType Directory -Force -Path $RuntimeDirectory | Out-Null
@@ -281,6 +405,11 @@ function Write-WatcherStatus {
281
405
  restart_attempts = $script:WatcherRestartAttempts
282
406
  checked_at = (Get-Date).ToUniversalTime().ToString("o")
283
407
  last_error = if ($LastError) { ($LastError -replace '[\r\n]+', ' ').Substring(0, [Math]::Min(500, ($LastError -replace '[\r\n]+', ' ').Length)) } else { $null }
408
+ project_root = $ProjectRoot
409
+ config_path = $ConfigPath
410
+ watcher_entry_path = $WatcherEntryPath
411
+ validator_module_path = (Join-Path $ProjectRoot "dist\assessments\assessmentStore.js")
412
+ runtime_build_layout = $RuntimeBuildLayout
284
413
  }
285
414
  $temporary = "$WatcherStatusFile.tmp"
286
415
  $payload | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath $temporary -Encoding UTF8
@@ -304,21 +433,30 @@ function Start-OwnedWatcherProcess {
304
433
  $script:WatcherHealthySince = $null
305
434
  $previousWatcherInstanceId = $env:PATCHWARDEN_WATCHER_INSTANCE_ID
306
435
  $previousWatcherLauncherPid = $env:PATCHWARDEN_WATCHER_LAUNCHER_PID
436
+ $previousToolProfile = $env:PATCHWARDEN_TOOL_PROFILE
307
437
  $previousXdgConfigHome = $env:XDG_CONFIG_HOME
308
438
  $previousPath = $env:PATH
439
+ $previousControlPlaneApiKey = $env:CONTROL_PLANE_API_KEY
440
+ $previousOwnerToken = $env:PATCHWARDEN_OWNER_TOKEN
309
441
  try {
310
442
  $env:PATCHWARDEN_WATCHER_INSTANCE_ID = $script:WatcherInstanceId
311
443
  $env:PATCHWARDEN_WATCHER_LAUNCHER_PID = [string]$PID
444
+ $env:PATCHWARDEN_TOOL_PROFILE = $ToolProfile
312
445
  $env:XDG_CONFIG_HOME = $OpencodeConfigHome
313
446
  if ($OpencodeBin) { $env:PATH = "$OpencodeBin;$env:PATH" }
447
+ Remove-Item Env:CONTROL_PLANE_API_KEY -ErrorAction SilentlyContinue
448
+ Remove-Item Env:PATCHWARDEN_OWNER_TOKEN -ErrorAction SilentlyContinue
314
449
  $script:WatcherProcess = Start-Process -FilePath $node `
315
- -ArgumentList @((Join-Path $ProjectRoot "dist\runner\watch.js")) `
450
+ -ArgumentList @($WatcherEntryPath) `
316
451
  -WorkingDirectory $ProjectRoot -PassThru -WindowStyle Hidden `
317
452
  -RedirectStandardOutput $stdout -RedirectStandardError $stderr
318
453
  } finally {
319
454
  if ($null -eq $previousWatcherInstanceId) { Remove-Item Env:PATCHWARDEN_WATCHER_INSTANCE_ID -ErrorAction SilentlyContinue } else { $env:PATCHWARDEN_WATCHER_INSTANCE_ID = $previousWatcherInstanceId }
320
455
  if ($null -eq $previousWatcherLauncherPid) { Remove-Item Env:PATCHWARDEN_WATCHER_LAUNCHER_PID -ErrorAction SilentlyContinue } else { $env:PATCHWARDEN_WATCHER_LAUNCHER_PID = $previousWatcherLauncherPid }
456
+ if ($null -eq $previousToolProfile) { Remove-Item Env:PATCHWARDEN_TOOL_PROFILE -ErrorAction SilentlyContinue } else { $env:PATCHWARDEN_TOOL_PROFILE = $previousToolProfile }
321
457
  if ($null -eq $previousXdgConfigHome) { Remove-Item Env:XDG_CONFIG_HOME -ErrorAction SilentlyContinue } else { $env:XDG_CONFIG_HOME = $previousXdgConfigHome }
458
+ if ($null -eq $previousControlPlaneApiKey) { Remove-Item Env:CONTROL_PLANE_API_KEY -ErrorAction SilentlyContinue } else { $env:CONTROL_PLANE_API_KEY = $previousControlPlaneApiKey }
459
+ if ($null -eq $previousOwnerToken) { Remove-Item Env:PATCHWARDEN_OWNER_TOKEN -ErrorAction SilentlyContinue } else { $env:PATCHWARDEN_OWNER_TOKEN = $previousOwnerToken }
322
460
  $env:PATH = $previousPath
323
461
  }
324
462
  Write-WatcherStatus -Status "starting"
@@ -333,6 +471,13 @@ function Start-PatchWardenWatcher {
333
471
  Write-Host "[watch] Existing watcher heartbeat is fresh; it remains external and will not be managed."
334
472
  return
335
473
  }
474
+ $lockRepair = Repair-StaleWatcherLock
475
+ if ($lockRepair -eq "expected_watcher_alive" -or $lockRepair -eq "unverified") {
476
+ $script:WatcherManaged = $false
477
+ Write-WatcherStatus -Status "external_stale" -LastError "A stale watcher lock could not be safely retired."
478
+ Write-Host "[watch] Stale watcher lock retained because process ownership could not be disproved."
479
+ return
480
+ }
336
481
  Start-OwnedWatcherProcess
337
482
  }
338
483
 
@@ -368,7 +513,12 @@ function Invoke-WatcherSupervisorTick {
368
513
 
369
514
  function Get-TunnelHealth {
370
515
  if (-not (Test-Path -LiteralPath $HealthUrlFile) -or -not (Test-Path -LiteralPath $PidFile)) { return $null }
371
- $output = (& $TunnelClientExe health --json --url-file $HealthUrlFile --pid-file $PidFile 2>&1 | Out-String)
516
+ $savedAgentEnvironment = Suspend-AgentProviderEnvironment
517
+ try {
518
+ $output = (& $TunnelClientExe health --json --url-file $HealthUrlFile --pid-file $PidFile 2>&1 | Out-String)
519
+ } finally {
520
+ Restore-AgentProviderEnvironment -Saved $savedAgentEnvironment
521
+ }
372
522
  try { return $output | ConvertFrom-Json } catch { return $null }
373
523
  }
374
524
 
@@ -505,8 +655,7 @@ if (-not $TunnelClientExe) {
505
655
 
506
656
  # Search common installation locations
507
657
  $commonPaths = @(
508
- $DefaultTunnelClientPath
509
- Join-Path $env:LOCALAPPDATA "patchwarden\tunnel-client.exe"
658
+ Join-Path $env:LOCALAPPDATA "PatchWarden\tunnel-client.exe"
510
659
  Join-Path $env:APPDATA "tunnel-client\tunnel-client.exe"
511
660
  Join-Path $env:USERPROFILE "tunnel-client\tunnel-client.exe"
512
661
  )
@@ -525,11 +674,8 @@ if (-not $TunnelClientExe) {
525
674
  Write-Host "[input] tunnel-client.exe not found on PATH or common locations." -ForegroundColor Yellow
526
675
  Write-Host " Please provide the full path."
527
676
  Write-Host ""
528
- Write-Host " To search for it manually, try one of these commands:" -ForegroundColor Cyan
529
- Write-Host " Get-ChildItem -Path `$env:LOCALAPPDATA -Recurse -Filter tunnel-client.exe -ErrorAction SilentlyContinue" -ForegroundColor Gray
530
- Write-Host " Get-ChildItem -Path `$env:APPDATA -Recurse -Filter tunnel-client.exe -ErrorAction SilentlyContinue" -ForegroundColor Gray
531
- Write-Host " Get-ChildItem -Path C:\ -Recurse -Filter tunnel-client.exe -ErrorAction SilentlyContinue" -ForegroundColor Gray
532
- Write-Host " Or download it from: https://platform.openai.com/docs/guides/mcp-connector" -ForegroundColor Cyan
677
+ Write-Host " Desktop users can open Settings -> MCP and tunnel -> Auto detect / Choose file." -ForegroundColor Cyan
678
+ Write-Host " The desktop guide explains trusted downloads and SHA256 verification." -ForegroundColor Cyan
533
679
  Write-Host ""
534
680
  $TunnelClientExe = Read-Host "Path to tunnel-client.exe"
535
681
  }
@@ -556,7 +702,7 @@ if (-not $TunnelId -and (Test-Path -LiteralPath $ProfilePath)) {
556
702
  $profileContent = Get-Content -LiteralPath $ProfilePath -Raw -Encoding UTF8
557
703
  if ($profileContent -match 'tunnel_id:\s*"([^"]+)"') {
558
704
  $TunnelId = $matches[1]
559
- Write-Host "[detect] Tunnel ID read from profile $Profile`: $TunnelId" -ForegroundColor Green
705
+ Write-Host "[detect] Tunnel ID read from profile $Profile." -ForegroundColor Green
560
706
  }
561
707
  } catch {
562
708
  Write-Host "[warn] Could not read Tunnel ID from $ProfilePath`: $($_.Exception.Message)" -ForegroundColor Yellow
@@ -580,8 +726,15 @@ if (-not (Test-Path -LiteralPath (Join-Path $ProjectRoot "dist\index.js"))) {
580
726
  New-Item -ItemType Directory -Force -Path $RuntimeDirectory | Out-Null
581
727
  $env:PATCHWARDEN_CONFIG = $ConfigPath
582
728
  Write-Host "[manifest] Verifying the exact $ToolProfile stdio MCP tool catalog..."
583
- $manifestOutput = (& node (Join-Path $ProjectRoot "scripts\checks\mcp-manifest-check.js") --profile $ToolProfile --json 2>&1 | Out-String).Trim()
584
- if ($LASTEXITCODE -ne 0) {
729
+ $previousErrorActionPreference = $ErrorActionPreference
730
+ try {
731
+ $ErrorActionPreference = "Continue"
732
+ $manifestOutput = (& node (Join-Path $ProjectRoot "scripts\checks\mcp-manifest-check.js") --profile $ToolProfile --json 2>&1 | Out-String).Trim()
733
+ $manifestExitCode = $LASTEXITCODE
734
+ } finally {
735
+ $ErrorActionPreference = $previousErrorActionPreference
736
+ }
737
+ if ($manifestExitCode -ne 0) {
585
738
  Write-TunnelStatus -Status "stopped" -ReasonCode "tool_manifest_check_failed" -LastError "The tunnel MCP tool manifest preflight failed."
586
739
  throw "Tool manifest preflight failed: $manifestOutput"
587
740
  }
@@ -601,10 +754,6 @@ Write-Host "[config] health listen addr: $HealthListenAddr"
601
754
  Set-SecretEnvIfMissing
602
755
 
603
756
  $env:PATCHWARDEN_CONFIG = $ConfigPath
604
- $env:HTTP_PROXY = $ProxyUrl
605
- $env:HTTPS_PROXY = $ProxyUrl
606
- $env:ALL_PROXY = $ProxyUrl
607
- $env:NO_PROXY = "localhost,127.0.0.1,::1"
608
757
 
609
758
  $profileNeedsInit = $true
610
759
  if (Test-Path -LiteralPath $ProfilePath) {
@@ -614,12 +763,17 @@ if (Test-Path -LiteralPath $ProfilePath) {
614
763
 
615
764
  if ($profileNeedsInit) {
616
765
  Write-Host "[init] Creating tunnel-client profile: $Profile"
617
- & $TunnelClientExe init `
618
- --sample sample_mcp_stdio_local `
619
- --profile $Profile `
620
- --tunnel-id $TunnelId `
621
- --mcp-command $McpStdioLauncherForTunnel `
622
- --force
766
+ $savedAgentEnvironment = Suspend-AgentProviderEnvironment
767
+ try {
768
+ & $TunnelClientExe init `
769
+ --sample sample_mcp_stdio_local `
770
+ --profile $Profile `
771
+ --tunnel-id $TunnelId `
772
+ --mcp-command $McpStdioLauncherForTunnel `
773
+ --force
774
+ } finally {
775
+ Restore-AgentProviderEnvironment -Saved $savedAgentEnvironment
776
+ }
623
777
  Set-ProfileHealthListenAddr -ProfilePath $ProfilePath -HealthListenAddr $HealthListenAddr
624
778
  } else {
625
779
  Set-ProfileHealthListenAddr -ProfilePath $ProfilePath -HealthListenAddr $HealthListenAddr
@@ -628,7 +782,7 @@ if ($profileNeedsInit) {
628
782
  $env:PATCHWARDEN_TUNNEL_STATUS_FILE = $StatusFile
629
783
  if (-not $SkipWatcher) { Start-PatchWardenWatcher }
630
784
 
631
- Write-Host "[doctor] Checking tunnel-client profile through the configured proxy..."
785
+ Write-Host "[doctor] Checking tunnel-client profile (proxy mode: $ProxyMode)..."
632
786
  $preflightAttempt = 0
633
787
  $preflightMaxRetries = 3
634
788
  $doctor = Invoke-TunnelDoctor
@@ -665,8 +819,7 @@ Write-Host "[run-config] health url file: $HealthUrlFile"
665
819
  Write-Host "[run-config] pid file: $PidFile"
666
820
  Write-Host "[run-config] stdout log: $StdoutLogFile"
667
821
  Write-Host "[run-config] stderr log: $StderrLogFile"
668
- Write-Host "[run-config] HTTPS_PROXY: $env:HTTPS_PROXY"
669
- Write-Host "[run-config] HTTP_PROXY: $env:HTTP_PROXY"
822
+ Write-Host "[run-config] proxy mode: $ProxyMode"
670
823
  Write-Host "[run-config] CONTROL_PLANE_API_KEY: $(if ($env:CONTROL_PLANE_API_KEY) { 'set' } else { 'missing' })"
671
824
  Write-Host "[run-config] cwd: $ProjectRoot"
672
825
  Write-Host ""
@@ -681,18 +834,23 @@ try {
681
834
  Write-TunnelStatus -Status "starting" -ReasonCode $null -Ready $false -Attempt $attempt
682
835
  $runArguments = @(
683
836
  "run", "--profile", $Profile,
684
- "--http-proxy", "env:HTTPS_PROXY",
685
837
  "--health.url-file", $HealthUrlFile,
686
838
  "--pid.file", $PidFile,
687
839
  "--log.format", "json"
688
840
  )
841
+ if ($script:UseProxyArgument) { $runArguments += @("--http-proxy", "env:HTTPS_PROXY") }
689
842
  if ($openUi) { $runArguments += "--open-web-ui"; $openUi = $false }
690
843
  $argumentLine = ($runArguments | ForEach-Object { Quote-ProcessArgument $_ }) -join " "
691
844
  Write-Host "[run-config] command: tunnel-client $($runArguments -join ' ')"
692
845
  $redirectedCommand = '""' + $TunnelClientExe + '" ' + $argumentLine + ' 1>"' + $StdoutLogFile + '" 2>"' + $StderrLogFile + '""'
693
846
  $processArgumentLine = '/d /s /c ' + $redirectedCommand
694
- $script:TunnelProcess = Start-Process -FilePath $env:ComSpec -ArgumentList $processArgumentLine `
695
- -WorkingDirectory $ProjectRoot -PassThru -WindowStyle Hidden
847
+ $savedAgentEnvironment = Suspend-AgentProviderEnvironment
848
+ try {
849
+ $script:TunnelProcess = Start-Process -FilePath $env:ComSpec -ArgumentList $processArgumentLine `
850
+ -WorkingDirectory $ProjectRoot -PassThru -WindowStyle Hidden
851
+ } finally {
852
+ Restore-AgentProviderEnvironment -Saved $savedAgentEnvironment
853
+ }
696
854
  Write-TunnelStatus -Status "connecting" -ReasonCode $null -Ready $false -Attempt $attempt -ProcessId $script:TunnelProcess.Id
697
855
  Write-Host "[run] supervised command PID $($script:TunnelProcess.Id), attempt $attempt"
698
856
 
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { join, resolve } from "node:path";
4
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
5
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
6
+
7
+ const runtimeRoot = resolve(process.env.PATCHWARDEN_RUNTIME_ROOT || join(import.meta.dirname, "..", "..", "release", "desktop", "win-unpacked", "resources", "core"));
8
+ const configPath = resolve(process.env.PATCHWARDEN_CONFIG || join(process.env.LOCALAPPDATA || "", "PatchWarden", "patchwarden.config.json"));
9
+ const fixture = resolve(process.env.PATCHWARDEN_DEMO_FIXTURE || join(process.env.PATCHWARDEN_WORKSPACE || "D:/ai_agent/patchwarden_program", "patchwarden-demo-fixture"));
10
+ const evidencePath = resolve(process.env.PATCHWARDEN_DEMO_EVIDENCE || join(process.env.PATCHWARDEN_WORKSPACE || "D:/ai_agent/patchwarden_program", ".patchwarden", "demo-runtime-evidence.json"));
11
+ const mode = process.argv[2] || "core-prepare";
12
+ const profile = mode === "direct" ? "chatgpt_direct" : "chatgpt_core";
13
+
14
+ function decode(result) {
15
+ if (result?.isError) {
16
+ const message = result.content?.find((item) => item.type === "text")?.text || "MCP tool error";
17
+ throw new Error(message);
18
+ }
19
+ const text = result?.content?.find((item) => item.type === "text")?.text;
20
+ if (typeof text !== "string") return result;
21
+ try { return JSON.parse(text); } catch { return { text }; }
22
+ }
23
+
24
+ async function connect() {
25
+ const transport = new StdioClientTransport({
26
+ command: process.execPath,
27
+ args: [join(runtimeRoot, "dist", "index.js")],
28
+ cwd: runtimeRoot,
29
+ env: {
30
+ ...process.env,
31
+ PATCHWARDEN_CONFIG: configPath,
32
+ PATCHWARDEN_TOOL_PROFILE: profile,
33
+ },
34
+ stderr: "pipe",
35
+ });
36
+ const client = new Client({ name: "patchwarden-demo-e2e", version: "1.0.0" }, { capabilities: {} });
37
+ await client.connect(transport);
38
+ return client;
39
+ }
40
+
41
+ async function call(client, name, args = {}) {
42
+ return decode(await client.callTool({ name, arguments: args }));
43
+ }
44
+
45
+ async function waitForTask(client, taskId) {
46
+ for (;;) {
47
+ const result = await call(client, "wait_for_task", { task_id: taskId, wait_seconds: 30 });
48
+ if (!result?.continuation_required) return result;
49
+ }
50
+ }
51
+
52
+ function saveEvidence(value) {
53
+ mkdirSync(resolve(evidencePath, ".."), { recursive: true });
54
+ writeFileSync(evidencePath, JSON.stringify(value, null, 2), "utf8");
55
+ }
56
+
57
+ function loadEvidence() {
58
+ return JSON.parse(readFileSync(evidencePath, "utf8"));
59
+ }
60
+
61
+ async function runCorePrepare(client) {
62
+ const health = await call(client, "health_check", { detail: "self_diagnostic" });
63
+ const agents = await call(client, "list_agents");
64
+ const ordinary = await call(client, "create_task", {
65
+ template: "inspect_only",
66
+ goal: "Read the Demo fixture README and package.json, run the configured tests, and return a short verification summary. Do not modify files.",
67
+ repo_path: "patchwarden-demo-fixture",
68
+ agent: "codex",
69
+ verify_commands: ["npm test", "npm run lint"],
70
+ timeout_seconds: 180,
71
+ });
72
+ const ordinarySummary = await waitForTask(client, ordinary.task_id);
73
+ const ordinaryAudit = await call(client, "audit_task", { task_id: ordinary.task_id });
74
+ const assessmentArgs = {
75
+ execution_mode: "assess_only",
76
+ template: "inspect_only",
77
+ goal: "Read the Demo fixture README and package.json, run npm test and npm run lint, and return a short verification summary. Do not modify files.",
78
+ repo_path: "patchwarden-demo-fixture",
79
+ agent: "codex",
80
+ verify_commands: ["npm test", "npm run lint"],
81
+ timeout_seconds: 180,
82
+ };
83
+ const assessmentBeforeRestart = await call(client, "create_task", assessmentArgs);
84
+ const assessmentPolicyChange = await call(client, "create_task", assessmentArgs);
85
+ saveEvidence({
86
+ created_at: new Date().toISOString(),
87
+ profile,
88
+ runtime_root: runtimeRoot,
89
+ config_path: configPath,
90
+ fixture,
91
+ health,
92
+ agents,
93
+ ordinary_task_id: ordinary.task_id,
94
+ ordinary_summary: ordinarySummary,
95
+ ordinary_audit: ordinaryAudit,
96
+ assessment_before_restart: assessmentBeforeRestart,
97
+ assessment_policy_change: assessmentPolicyChange,
98
+ });
99
+ console.log(JSON.stringify({ ordinary_task_id: ordinary.task_id, assessment_before_restart: assessmentBeforeRestart, assessment_policy_change: assessmentPolicyChange }, null, 2));
100
+ }
101
+
102
+ async function runCoreExecute(client) {
103
+ const evidence = loadEvidence();
104
+ const first = evidence.assessment_before_restart;
105
+ const second = evidence.assessment_policy_change;
106
+ const execute = await call(client, "create_task", { execution_mode: "execute", assessment_id: first.assessment_id });
107
+ const summary = await waitForTask(client, execute.task_id);
108
+ const audit = await call(client, "audit_task", { task_id: execute.task_id });
109
+ const policyDir = join(fixture, ".patchwarden");
110
+ mkdirSync(policyDir, { recursive: true });
111
+ writeFileSync(join(policyDir, "project-policy.json"), JSON.stringify({ allowed_commands: ["node --check main.js"] }, null, 2), "utf8");
112
+ let stale = null;
113
+ try {
114
+ stale = await call(client, "create_task", { execution_mode: "execute", assessment_id: second.assessment_id });
115
+ } catch (error) {
116
+ stale = { error: error instanceof Error ? error.message : String(error) };
117
+ }
118
+ saveEvidence({ ...evidence, execute_task_id: execute.task_id, execute_summary: summary, execute_audit: audit, stale_after_policy_change: stale });
119
+ console.log(JSON.stringify({ execute_task_id: execute.task_id, execute_summary: summary, execute_audit: audit, stale_after_policy_change: stale }, null, 2));
120
+ }
121
+
122
+ async function runDirect(client) {
123
+ const health = await call(client, "health_check", { detail: "self_diagnostic" });
124
+ const session = await call(client, "create_direct_session", { repo_path: "patchwarden-demo-fixture", title: "PatchWarden Demo Direct verification" });
125
+ const file = await call(client, "read_workspace_file", { session_id: session.session_id, path: "main.js" });
126
+ const verification = await call(client, "run_direct_verification_bundle", {
127
+ session_id: session.session_id,
128
+ commands: ["npm test", "npm run lint", "node --check main.js"],
129
+ timeout_seconds: 120,
130
+ });
131
+ const finalized = await call(client, "finalize_direct_session", { session_id: session.session_id });
132
+ const audit = await call(client, "audit_session", { session_id: session.session_id });
133
+ saveEvidence({ direct_health: health, direct_session_id: session.session_id, direct_file: { sha256: file.sha256, bytes: file.bytes }, direct_verification: verification, direct_finalized: finalized, direct_audit: audit });
134
+ console.log(JSON.stringify({ direct_session_id: session.session_id, direct_verification: verification, direct_audit: audit }, null, 2));
135
+ }
136
+
137
+ async function runStatus(client) {
138
+ const health = await call(client, "health_check", { detail: "self_diagnostic" });
139
+ const agents = await call(client, "list_agents");
140
+ const tasks = await call(client, "list_tasks", { active_only: true, limit: 50 });
141
+ console.log(JSON.stringify({ health, agents, active_tasks: tasks }, null, 2));
142
+ }
143
+
144
+ const client = await connect();
145
+ try {
146
+ if (mode === "core-prepare") await runCorePrepare(client);
147
+ else if (mode === "core-execute") await runCoreExecute(client);
148
+ else if (mode === "direct") await runDirect(client);
149
+ else if (mode === "status") await runStatus(client);
150
+ else throw new Error(`Unsupported demo mode: ${mode}`);
151
+ } finally {
152
+ await client.close();
153
+ }
@@ -1,11 +1,11 @@
1
1
  @echo off
2
2
  setlocal
3
- cd /d "%~dp0"
3
+ cd /d "%~dp0..\.."
4
4
  if /i "%~1"=="--foreground" (
5
- powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0scripts\control\control-center-tray.ps1" %*
5
+ powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0..\control\control-center-tray.ps1" %*
6
6
  set EXITCODE=%ERRORLEVEL%
7
7
  endlocal & exit /b %EXITCODE%
8
8
  )
9
9
 
10
- start "" powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "%~dp0scripts\control\control-center-tray.ps1" %*
10
+ start "" powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "%~dp0..\control\control-center-tray.ps1" %*
11
11
  endlocal & exit /b 0
@@ -0,0 +1,6 @@
1
+ @echo off
2
+ setlocal
3
+ cd /d "%~dp0..\.."
4
+ powershell -ExecutionPolicy Bypass -NoProfile -File "%~dp0..\control\start-control-center.ps1" %*
5
+ set EXITCODE=%ERRORLEVEL%
6
+ endlocal & exit /b %EXITCODE%
@@ -1,5 +1,5 @@
1
1
  @echo off
2
2
  setlocal
3
- cd /d "%~dp0"
4
- start "" powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "%~dp0scripts\control\control-center-tray.ps1" %*
3
+ cd /d "%~dp0..\.."
4
+ start "" powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "%~dp0..\control\control-center-tray.ps1" %*
5
5
  endlocal & exit /b 0
@@ -0,0 +1,6 @@
1
+ @echo off
2
+ setlocal
3
+ cd /d "%~dp0..\.."
4
+ powershell -ExecutionPolicy Bypass -NoProfile -File "%~dp0..\control\restart-control-center.ps1" %*
5
+ set EXITCODE=%ERRORLEVEL%
6
+ endlocal & exit /b %EXITCODE%
@@ -1,7 +1,7 @@
1
1
  @echo off
2
2
  setlocal
3
- cd /d "%~dp0"
4
- powershell -ExecutionPolicy Bypass -NoProfile -File "%~dp0scripts\control\stop-patchwarden.ps1" %*
3
+ cd /d "%~dp0..\.."
4
+ powershell -ExecutionPolicy Bypass -NoProfile -File "%~dp0..\control\stop-patchwarden.ps1" %*
5
5
  set EXITCODE=%ERRORLEVEL%
6
6
  if not "%EXITCODE%"=="0" (
7
7
  echo.
@@ -0,0 +1,11 @@
1
+ import { rmSync } from "node:fs";
2
+ import { basename, dirname, resolve } from "node:path";
3
+
4
+ export function cleanGeneratedOutput(projectRoot, outputPath) {
5
+ const root = resolve(projectRoot);
6
+ const output = resolve(outputPath);
7
+ if (dirname(output) !== root || basename(output).toLowerCase() !== "dist") {
8
+ throw new Error(`Refusing to clean non-dist output: ${output}`);
9
+ }
10
+ rmSync(output, { recursive: true, force: true });
11
+ }
@@ -0,0 +1,12 @@
1
+ import { readdirSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+
4
+ export function collectMatchingFiles(directory, predicate) {
5
+ return readdirSync(directory, { withFileTypes: true })
6
+ .sort((left, right) => left.name.localeCompare(right.name))
7
+ .flatMap((entry) => {
8
+ const path = resolve(directory, entry.name);
9
+ if (entry.isDirectory()) return collectMatchingFiles(path, predicate);
10
+ return entry.isFile() && predicate(entry.name, path) ? [path] : [];
11
+ });
12
+ }