patchwarden 1.5.1 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (591) hide show
  1. package/PatchWarden.cmd +32 -2
  2. package/README.en.md +158 -26
  3. package/README.md +124 -24
  4. package/dist/assessments/agentAssessor.js +21 -12
  5. package/dist/assessments/assessmentDiagnostics.d.ts +29 -0
  6. package/dist/assessments/assessmentDiagnostics.js +132 -0
  7. package/dist/assessments/assessmentStore.d.ts +39 -0
  8. package/dist/assessments/assessmentStore.js +207 -45
  9. package/dist/assessments/securitySnapshot.d.ts +50 -0
  10. package/dist/assessments/securitySnapshot.js +158 -0
  11. package/dist/config.d.ts +15 -0
  12. package/dist/config.js +66 -13
  13. package/dist/control/fileManager.d.ts +2 -0
  14. package/dist/control/fileManager.js +39 -0
  15. package/dist/control/middleware/auth.d.ts +6 -0
  16. package/dist/control/middleware/auth.js +14 -5
  17. package/dist/control/middleware/static.js +16 -3
  18. package/dist/control/routeTable.d.ts +24 -0
  19. package/dist/control/routeTable.js +263 -0
  20. package/dist/control/routes/audit.js +17 -7
  21. package/dist/control/routes/evidence.js +1 -1
  22. package/dist/control/routes/lineage.js +1 -1
  23. package/dist/control/routes/process.d.ts +7 -0
  24. package/dist/control/routes/process.js +329 -42
  25. package/dist/control/routes/sessions.d.ts +9 -1
  26. package/dist/control/routes/sessions.js +84 -21
  27. package/dist/control/routes/status.d.ts +60 -1
  28. package/dist/control/routes/status.js +94 -15
  29. package/dist/control/routes/taskActions.js +24 -60
  30. package/dist/control/routes/tasks.d.ts +4 -0
  31. package/dist/control/routes/tasks.js +61 -62
  32. package/dist/control/routes/workspace.js +50 -23
  33. package/dist/control/runtime.d.ts +3 -2
  34. package/dist/control/runtime.js +65 -40
  35. package/dist/control/server.js +37 -365
  36. package/dist/control/shared.d.ts +25 -1
  37. package/dist/control/shared.js +64 -11
  38. package/dist/diagnostics/allowedTestCommandSafety.d.ts +1 -0
  39. package/dist/diagnostics/allowedTestCommandSafety.js +11 -0
  40. package/dist/direct/directAudit.js +234 -225
  41. package/dist/direct/directGuards.d.ts +2 -1
  42. package/dist/direct/directGuards.js +42 -29
  43. package/dist/direct/directPatch.d.ts +6 -1
  44. package/dist/direct/directPatch.js +55 -8
  45. package/dist/direct/directSessionStore.d.ts +16 -6
  46. package/dist/direct/directSessionStore.js +128 -35
  47. package/dist/direct/directVerification.js +8 -16
  48. package/dist/doctor.js +65 -57
  49. package/dist/goal/acceptanceEngine.d.ts +1 -1
  50. package/dist/goal/goalProgress.js +75 -69
  51. package/dist/goal/goalReport.js +5 -12
  52. package/dist/goal/goalStatus.d.ts +2 -0
  53. package/dist/goal/goalStatus.js +10 -3
  54. package/dist/goal/goalStore.d.ts +9 -2
  55. package/dist/goal/goalStore.js +152 -45
  56. package/dist/goal/handoffExport.js +6 -6
  57. package/dist/goal/specKitImport.js +97 -96
  58. package/dist/goal/subgoalSync.js +11 -9
  59. package/dist/goal/worktreeManager.d.ts +2 -0
  60. package/dist/goal/worktreeManager.js +209 -136
  61. package/dist/httpServer.js +111 -32
  62. package/dist/logging.js +5 -3
  63. package/dist/policy/projectPolicy.js +5 -2
  64. package/dist/release/releaseGate.d.ts +1 -1
  65. package/dist/release/releaseGate.js +78 -25
  66. package/dist/runner/agentInvocation.d.ts +14 -0
  67. package/dist/runner/agentInvocation.js +96 -4
  68. package/dist/runner/changeCapture.d.ts +13 -5
  69. package/dist/runner/changeCapture.js +243 -61
  70. package/dist/runner/postTaskCleanup.js +48 -19
  71. package/dist/runner/processSecurity.d.ts +56 -0
  72. package/dist/runner/processSecurity.js +373 -0
  73. package/dist/runner/runTask.d.ts +2 -1
  74. package/dist/runner/runTask.js +162 -93
  75. package/dist/runner/simpleProcess.d.ts +3 -0
  76. package/dist/runner/simpleProcess.js +50 -36
  77. package/dist/{taskProgress.d.ts → runner/taskProgress.d.ts} +1 -1
  78. package/dist/{taskProgress.js → runner/taskProgress.js} +2 -2
  79. package/dist/{taskRuntime.d.ts → runner/taskRuntime.d.ts} +3 -1
  80. package/dist/{taskRuntime.js → runner/taskRuntime.js} +3 -2
  81. package/dist/runner/taskStatusStore.d.ts +11 -0
  82. package/dist/runner/taskStatusStore.js +35 -0
  83. package/dist/runner/watch.d.ts +14 -1
  84. package/dist/runner/watch.js +320 -49
  85. package/dist/security/contentRedaction.js +2 -2
  86. package/dist/security/discoveryTokenStore.d.ts +3 -2
  87. package/dist/security/discoveryTokenStore.js +17 -2
  88. package/dist/security/loopbackHost.d.ts +2 -0
  89. package/dist/security/loopbackHost.js +16 -0
  90. package/dist/security/pathGuard.js +32 -17
  91. package/dist/security/planGuard.js +2 -1
  92. package/dist/security/runtimeGuard.js +9 -6
  93. package/dist/security/secretComparison.d.ts +3 -0
  94. package/dist/security/secretComparison.js +11 -0
  95. package/dist/security/sensitiveGuard.d.ts +5 -0
  96. package/dist/security/sensitiveGuard.js +27 -8
  97. package/dist/security/toolInvocationGuard.d.ts +2 -2
  98. package/dist/security/toolInvocationGuard.js +1 -1
  99. package/dist/security/workspaceRootGuard.d.ts +8 -0
  100. package/dist/security/workspaceRootGuard.js +47 -0
  101. package/dist/smoke-test.js +27 -26
  102. package/dist/tools/catalog/index.d.ts +4 -0
  103. package/dist/tools/catalog/index.js +4 -0
  104. package/dist/tools/{toolCatalog.js → catalog/toolCatalog.js} +3 -12
  105. package/dist/tools/{toolRegistry.d.ts → catalog/toolRegistry.d.ts} +3 -3
  106. package/dist/tools/{toolRegistry.js → catalog/toolRegistry.js} +6 -15
  107. package/dist/tools/definitions/toolDefs.d.ts +18 -0
  108. package/dist/tools/definitions/toolDefs.js +1177 -0
  109. package/dist/tools/{auditSession.d.ts → diagnostics/auditSession.d.ts} +1 -1
  110. package/dist/tools/{auditSession.js → diagnostics/auditSession.js} +2 -2
  111. package/dist/tools/{auditTask.d.ts → diagnostics/auditTask.d.ts} +2 -1
  112. package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +134 -36
  113. package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +18 -6
  114. package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +36 -8
  115. package/dist/tools/diagnostics/index.d.ts +6 -0
  116. package/dist/tools/diagnostics/index.js +6 -0
  117. package/dist/tools/{safeStatus.d.ts → diagnostics/safeStatus.d.ts} +3 -3
  118. package/dist/tools/{safeStatus.js → diagnostics/safeStatus.js} +6 -6
  119. package/dist/tools/{safeViews.d.ts → diagnostics/safeViews.d.ts} +12 -8
  120. package/dist/tools/{safeViews.js → diagnostics/safeViews.js} +23 -13
  121. package/dist/tools/{schemaDriftCheck.d.ts → diagnostics/schemaDriftCheck.d.ts} +1 -1
  122. package/dist/tools/{schemaDriftCheck.js → diagnostics/schemaDriftCheck.js} +2 -2
  123. package/dist/tools/{createDirectSession.js → direct/createDirectSession.js} +6 -6
  124. package/dist/tools/{finalizeDirectSession.d.ts → direct/finalizeDirectSession.d.ts} +1 -1
  125. package/dist/tools/direct/finalizeDirectSession.js +86 -0
  126. package/dist/tools/direct/index.d.ts +3 -0
  127. package/dist/tools/direct/index.js +3 -0
  128. package/dist/tools/{runDirectVerificationBundle.js → direct/runDirectVerificationBundle.js} +2 -2
  129. package/dist/tools/{discoverTools.d.ts → discovery/discoverTools.d.ts} +2 -2
  130. package/dist/tools/{discoverTools.js → discovery/discoverTools.js} +5 -5
  131. package/dist/tools/{explainTool.d.ts → discovery/explainTool.d.ts} +2 -2
  132. package/dist/tools/{explainTool.js → discovery/explainTool.js} +2 -2
  133. package/dist/tools/discovery/index.d.ts +3 -0
  134. package/dist/tools/discovery/index.js +3 -0
  135. package/dist/tools/{invokeDiscoveredTool.d.ts → discovery/invokeDiscoveredTool.d.ts} +2 -2
  136. package/dist/tools/{invokeDiscoveredTool.js → discovery/invokeDiscoveredTool.js} +5 -5
  137. package/dist/tools/dispatch/coreDispatch.js +32 -31
  138. package/dist/tools/dispatch/diagnosticDispatch.d.ts +3 -5
  139. package/dist/tools/dispatch/diagnosticDispatch.js +70 -68
  140. package/dist/tools/dispatch/directDispatch.js +11 -10
  141. package/dist/tools/dispatch/goalDispatch.js +5 -4
  142. package/dist/tools/dispatch/releaseDispatch.js +3 -2
  143. package/dist/tools/dispatch/validation.d.ts +7 -0
  144. package/dist/tools/dispatch/validation.js +71 -0
  145. package/dist/tools/{getPlan.js → goals/getPlan.js} +4 -4
  146. package/dist/tools/{goalSubgoalTask.d.ts → goals/goalSubgoalTask.d.ts} +1 -1
  147. package/dist/tools/goals/goalSubgoalTask.js +118 -0
  148. package/dist/tools/goals/index.d.ts +3 -0
  149. package/dist/tools/goals/index.js +3 -0
  150. package/dist/tools/{savePlan.js → goals/savePlan.js} +28 -8
  151. package/dist/tools/registry.d.ts +7 -11
  152. package/dist/tools/registry.js +17 -1178
  153. package/dist/tools/{checkReleaseGate.d.ts → release/checkReleaseGate.d.ts} +1 -1
  154. package/dist/tools/{checkReleaseGate.js → release/checkReleaseGate.js} +3 -3
  155. package/dist/tools/release/index.d.ts +2 -0
  156. package/dist/tools/release/index.js +2 -0
  157. package/dist/tools/{releaseMode.d.ts → release/releaseMode.d.ts} +2 -2
  158. package/dist/tools/{releaseMode.js → release/releaseMode.js} +46 -26
  159. package/dist/tools/tasks/cancelTask.d.ts +11 -0
  160. package/dist/tools/tasks/cancelTask.js +86 -0
  161. package/dist/tools/{createTask.d.ts → tasks/createTask.d.ts} +12 -4
  162. package/dist/tools/{createTask.js → tasks/createTask.js} +110 -34
  163. package/dist/tools/{diagnoseTask.d.ts → tasks/diagnoseTask.d.ts} +1 -1
  164. package/dist/tools/{diagnoseTask.js → tasks/diagnoseTask.js} +12 -16
  165. package/dist/tools/{evidencePack.js → tasks/evidencePack.js} +34 -22
  166. package/dist/tools/{getTaskFile.js → tasks/getTaskFile.js} +4 -4
  167. package/dist/tools/{getTaskStatus.d.ts → tasks/getTaskStatus.d.ts} +6 -1
  168. package/dist/tools/{getTaskStatus.js → tasks/getTaskStatus.js} +11 -6
  169. package/dist/tools/{getTaskStdoutTail.js → tasks/getTaskStdoutTail.js} +13 -11
  170. package/dist/tools/{getTaskSummary.js → tasks/getTaskSummary.js} +35 -36
  171. package/dist/tools/tasks/index.d.ts +18 -0
  172. package/dist/tools/tasks/index.js +18 -0
  173. package/dist/tools/tasks/killTask.d.ts +1 -0
  174. package/dist/tools/{listTasks.d.ts → tasks/listTasks.d.ts} +1 -1
  175. package/dist/tools/{listTasks.js → tasks/listTasks.js} +5 -4
  176. package/dist/tools/{reconcileTasks.d.ts → tasks/reconcileTasks.d.ts} +3 -2
  177. package/dist/tools/tasks/reconcileTasks.js +496 -0
  178. package/dist/tools/tasks/retryTask.js +60 -0
  179. package/dist/tools/{runTaskLoop.d.ts → tasks/runTaskLoop.d.ts} +6 -6
  180. package/dist/tools/{runTaskLoop.js → tasks/runTaskLoop.js} +63 -41
  181. package/dist/tools/tasks/runVerification.js +38 -0
  182. package/dist/tools/{taskLineage.js → tasks/taskLineage.js} +8 -7
  183. package/dist/tools/{taskOutputs.d.ts → tasks/taskOutputs.d.ts} +2 -2
  184. package/dist/tools/{taskOutputs.js → tasks/taskOutputs.js} +17 -16
  185. package/dist/tools/tasks/taskStates.d.ts +6 -0
  186. package/dist/tools/tasks/taskStates.js +52 -0
  187. package/dist/tools/{waitForTask.js → tasks/waitForTask.js} +3 -11
  188. package/dist/tools/{androidDoctor.js → workspace/androidDoctor.js} +16 -10
  189. package/dist/tools/{applyPatch.d.ts → workspace/applyPatch.d.ts} +1 -1
  190. package/dist/tools/workspace/applyPatch.js +53 -0
  191. package/dist/tools/{discardWorktree.js → workspace/discardWorktree.js} +3 -3
  192. package/dist/tools/workspace/index.d.ts +10 -0
  193. package/dist/tools/workspace/index.js +10 -0
  194. package/dist/tools/workspace/listAgents.d.ts +23 -0
  195. package/dist/tools/workspace/listAgents.js +56 -0
  196. package/dist/tools/{listWorkspace.js → workspace/listWorkspace.js} +3 -3
  197. package/dist/tools/{mergeWorktree.js → workspace/mergeWorktree.js} +3 -3
  198. package/dist/tools/{readWorkspaceFile.js → workspace/readWorkspaceFile.js} +8 -8
  199. package/dist/tools/{recommendAgentForTask.js → workspace/recommendAgentForTask.js} +3 -3
  200. package/dist/tools/{searchWorkspace.js → workspace/searchWorkspace.js} +5 -5
  201. package/dist/tools/{syncFile.d.ts → workspace/syncFile.d.ts} +1 -1
  202. package/dist/tools/workspace/syncFile.js +134 -0
  203. package/dist/utils/atomicFile.d.ts +7 -0
  204. package/dist/utils/atomicFile.js +67 -0
  205. package/dist/utils/boundedFile.d.ts +8 -0
  206. package/dist/utils/boundedFile.js +93 -0
  207. package/dist/utils/lockedJsonFile.d.ts +15 -0
  208. package/dist/utils/lockedJsonFile.js +224 -0
  209. package/dist/utils/platform.d.ts +11 -0
  210. package/dist/utils/platform.js +26 -0
  211. package/dist/utils/stableJson.d.ts +2 -0
  212. package/dist/utils/stableJson.js +12 -0
  213. package/dist/version.d.ts +3 -2
  214. package/dist/version.js +3 -2
  215. package/dist/watcherStatus.d.ts +2 -2
  216. package/dist/watcherStatus.js +18 -0
  217. package/docs/CODE_WIKI.md +808 -143
  218. package/docs/architecture.md +15 -55
  219. package/docs/assets/patchwarden-desktop-onboarding.png +0 -0
  220. package/docs/control-center/README.md +4 -4
  221. package/docs/dashboard-overview.md +4 -4
  222. package/docs/desktop-app.md +175 -0
  223. package/docs/evidence-pack-schema.md +3 -3
  224. package/docs/open-source-application.md +16 -11
  225. package/docs/release-checklist.md +1 -1
  226. package/docs/release-evidence.md +35 -39
  227. package/docs/task-safe-review-workflow.md +5 -5
  228. package/docs/threat-model.md +19 -1
  229. package/examples/config.example.json +4 -2
  230. package/package.json +27 -10
  231. package/scripts/README.md +15 -6
  232. package/scripts/build.js +11 -0
  233. package/scripts/checks/brand-check.js +3 -0
  234. package/scripts/checks/build-output-check.js +37 -0
  235. package/scripts/checks/control-center-smoke.js +102 -10
  236. package/scripts/checks/control-smoke.js +19 -8
  237. package/scripts/checks/http-mcp-smoke.js +82 -4
  238. package/scripts/checks/lifecycle-smoke.js +15 -12
  239. package/scripts/checks/mcp-manifest-check.js +1 -1
  240. package/scripts/checks/mcp-smoke.js +58 -4
  241. package/scripts/checks/package-manifest-check.js +20 -7
  242. package/scripts/checks/unit-tests.js +3 -5
  243. package/scripts/checks/watcher-supervisor-smoke.js +38 -1
  244. package/scripts/control/get-patchwarden-health.ps1 +1 -1
  245. package/scripts/control/manage-patchwarden.ps1 +69 -4
  246. package/scripts/control/provision-patchwarden-tunnel.ps1 +127 -0
  247. package/scripts/control/run-background-supervisor.ps1 +41 -0
  248. package/scripts/control/start-patchwarden-tunnel.ps1 +191 -33
  249. package/scripts/e2e/demo-runtime.mjs +153 -0
  250. package/{PatchWarden-Control-Tray.cmd → scripts/launchers/PatchWarden-Control-Tray.cmd} +3 -3
  251. package/scripts/launchers/PatchWarden-Control.cmd +6 -0
  252. package/{PatchWarden-Desktop.cmd → scripts/launchers/PatchWarden-Desktop.cmd} +2 -2
  253. package/scripts/launchers/Restart-PatchWarden-Control.cmd +6 -0
  254. package/{Stop-PatchWarden.cmd → scripts/launchers/Stop-PatchWarden.cmd} +2 -2
  255. package/scripts/lib/clean-generated-output.js +11 -0
  256. package/scripts/lib/file-discovery.js +12 -0
  257. package/scripts/release/desktop-preflight.js +211 -0
  258. package/scripts/release/pack-clean.js +59 -14
  259. package/src/assessments/agentAssessor.ts +26 -12
  260. package/src/assessments/assessmentDiagnostics.ts +172 -0
  261. package/src/assessments/assessmentStore.ts +288 -51
  262. package/src/assessments/securitySnapshot.ts +231 -0
  263. package/src/config.ts +70 -13
  264. package/src/control/fileManager.ts +40 -0
  265. package/src/control/middleware/auth.ts +22 -4
  266. package/src/control/middleware/static.ts +16 -3
  267. package/src/control/routeTable.ts +301 -0
  268. package/src/control/routes/audit.ts +14 -6
  269. package/src/control/routes/evidence.ts +1 -1
  270. package/src/control/routes/lineage.ts +1 -1
  271. package/src/control/routes/process.ts +341 -40
  272. package/src/control/routes/sessions.ts +88 -25
  273. package/src/control/routes/status.ts +134 -17
  274. package/src/control/routes/taskActions.ts +24 -58
  275. package/src/control/routes/tasks.ts +66 -58
  276. package/src/control/routes/workspace.ts +56 -25
  277. package/src/control/runtime.ts +71 -43
  278. package/src/control/server.ts +37 -342
  279. package/src/control/shared.ts +77 -11
  280. package/src/diagnostics/allowedTestCommandSafety.ts +12 -0
  281. package/src/direct/directAudit.ts +17 -9
  282. package/src/direct/directGuards.ts +72 -44
  283. package/src/direct/directPatch.ts +95 -8
  284. package/src/direct/directSessionStore.ts +213 -55
  285. package/src/direct/directVerification.ts +9 -23
  286. package/src/doctor.ts +76 -64
  287. package/src/goal/acceptanceEngine.ts +1 -1
  288. package/src/goal/goalProgress.ts +17 -13
  289. package/src/goal/goalReport.ts +5 -13
  290. package/src/goal/goalStatus.ts +19 -3
  291. package/src/goal/goalStore.ts +241 -54
  292. package/src/goal/handoffExport.ts +6 -6
  293. package/src/goal/specKitImport.ts +12 -12
  294. package/src/goal/subgoalSync.ts +11 -8
  295. package/src/goal/worktreeManager.ts +139 -22
  296. package/src/httpServer.ts +113 -30
  297. package/src/logging.ts +10 -3
  298. package/src/policy/projectPolicy.ts +5 -2
  299. package/src/release/releaseGate.ts +74 -27
  300. package/src/runner/agentInvocation.ts +126 -4
  301. package/src/runner/changeCapture.ts +291 -57
  302. package/src/runner/postTaskCleanup.ts +41 -17
  303. package/src/runner/processSecurity.ts +433 -0
  304. package/src/runner/runTask.ts +197 -97
  305. package/src/runner/simpleProcess.ts +66 -31
  306. package/src/{taskProgress.ts → runner/taskProgress.ts} +3 -3
  307. package/src/{taskRuntime.ts → runner/taskRuntime.ts} +6 -3
  308. package/src/runner/taskStatusStore.ts +69 -0
  309. package/src/runner/watch.ts +348 -52
  310. package/src/security/contentRedaction.ts +2 -2
  311. package/src/security/discoveryTokenStore.ts +18 -4
  312. package/src/security/loopbackHost.ts +19 -0
  313. package/src/security/pathGuard.ts +35 -16
  314. package/src/security/planGuard.ts +2 -1
  315. package/src/security/runtimeGuard.ts +9 -6
  316. package/src/security/secretComparison.ts +13 -0
  317. package/src/security/sensitiveGuard.ts +27 -8
  318. package/src/security/toolInvocationGuard.ts +2 -2
  319. package/src/security/workspaceRootGuard.ts +53 -0
  320. package/src/smoke-test.ts +27 -26
  321. package/src/tools/catalog/index.ts +4 -0
  322. package/src/tools/{toolCatalog.ts → catalog/toolCatalog.ts} +3 -12
  323. package/src/tools/{toolRegistry.ts → catalog/toolRegistry.ts} +7 -16
  324. package/src/tools/definitions/toolDefs.ts +1264 -0
  325. package/src/tools/{auditSession.ts → diagnostics/auditSession.ts} +2 -2
  326. package/src/tools/{auditTask.ts → diagnostics/auditTask.ts} +136 -39
  327. package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +37 -9
  328. package/src/tools/diagnostics/index.ts +6 -0
  329. package/src/tools/{safeStatus.ts → diagnostics/safeStatus.ts} +7 -7
  330. package/src/tools/{safeViews.ts → diagnostics/safeViews.ts} +27 -17
  331. package/src/tools/{schemaDriftCheck.ts → diagnostics/schemaDriftCheck.ts} +3 -3
  332. package/src/tools/{createDirectSession.ts → direct/createDirectSession.ts} +6 -6
  333. package/src/tools/{finalizeDirectSession.ts → direct/finalizeDirectSession.ts} +7 -5
  334. package/src/tools/direct/index.ts +3 -0
  335. package/src/tools/{runDirectVerificationBundle.ts → direct/runDirectVerificationBundle.ts} +2 -2
  336. package/src/tools/{discoverTools.ts → discovery/discoverTools.ts} +6 -6
  337. package/src/tools/{explainTool.ts → discovery/explainTool.ts} +3 -3
  338. package/src/tools/discovery/index.ts +3 -0
  339. package/src/tools/{invokeDiscoveredTool.ts → discovery/invokeDiscoveredTool.ts} +7 -7
  340. package/src/tools/dispatch/coreDispatch.ts +36 -31
  341. package/src/tools/dispatch/diagnosticDispatch.ts +26 -21
  342. package/src/tools/dispatch/directDispatch.ts +11 -10
  343. package/src/tools/dispatch/goalDispatch.ts +5 -4
  344. package/src/tools/dispatch/releaseDispatch.ts +3 -2
  345. package/src/tools/dispatch/validation.ts +78 -0
  346. package/src/tools/{getPlan.ts → goals/getPlan.ts} +4 -4
  347. package/src/tools/{goalSubgoalTask.ts → goals/goalSubgoalTask.ts} +41 -26
  348. package/src/tools/goals/index.ts +3 -0
  349. package/src/tools/{savePlan.ts → goals/savePlan.ts} +38 -8
  350. package/src/tools/registry.ts +17 -1261
  351. package/src/tools/{checkReleaseGate.ts → release/checkReleaseGate.ts} +3 -3
  352. package/src/tools/release/index.ts +2 -0
  353. package/src/tools/{releaseMode.ts → release/releaseMode.ts} +42 -24
  354. package/src/tools/tasks/cancelTask.ts +110 -0
  355. package/src/tools/{createTask.ts → tasks/createTask.ts} +161 -33
  356. package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +12 -16
  357. package/src/tools/{evidencePack.ts → tasks/evidencePack.ts} +36 -22
  358. package/src/tools/{getTaskFile.ts → tasks/getTaskFile.ts} +4 -4
  359. package/src/tools/{getTaskStatus.ts → tasks/getTaskStatus.ts} +13 -6
  360. package/src/tools/{getTaskStdoutTail.ts → tasks/getTaskStdoutTail.ts} +13 -11
  361. package/src/tools/{getTaskSummary.ts → tasks/getTaskSummary.ts} +46 -44
  362. package/src/tools/tasks/index.ts +18 -0
  363. package/src/tools/{listTasks.ts → tasks/listTasks.ts} +6 -5
  364. package/src/tools/{reconcileTasks.ts → tasks/reconcileTasks.ts} +228 -77
  365. package/src/tools/tasks/retryTask.ts +69 -0
  366. package/src/tools/{runTaskLoop.ts → tasks/runTaskLoop.ts} +73 -46
  367. package/src/tools/{runVerification.ts → tasks/runVerification.ts} +6 -3
  368. package/src/tools/{taskLineage.ts → tasks/taskLineage.ts} +8 -7
  369. package/src/tools/{taskOutputs.ts → tasks/taskOutputs.ts} +27 -20
  370. package/src/tools/tasks/taskStates.ts +54 -0
  371. package/src/tools/{waitForTask.ts → tasks/waitForTask.ts} +3 -12
  372. package/src/tools/{androidDoctor.ts → workspace/androidDoctor.ts} +15 -10
  373. package/src/tools/{applyPatch.ts → workspace/applyPatch.ts} +38 -16
  374. package/src/tools/{discardWorktree.ts → workspace/discardWorktree.ts} +3 -3
  375. package/src/tools/workspace/index.ts +10 -0
  376. package/src/tools/workspace/listAgents.ts +75 -0
  377. package/src/tools/{listWorkspace.ts → workspace/listWorkspace.ts} +3 -3
  378. package/src/tools/{mergeWorktree.ts → workspace/mergeWorktree.ts} +3 -3
  379. package/src/tools/{readWorkspaceFile.ts → workspace/readWorkspaceFile.ts} +8 -8
  380. package/src/tools/{recommendAgentForTask.ts → workspace/recommendAgentForTask.ts} +3 -3
  381. package/src/tools/{searchWorkspace.ts → workspace/searchWorkspace.ts} +5 -5
  382. package/src/tools/workspace/syncFile.ts +292 -0
  383. package/src/utils/atomicFile.ts +74 -0
  384. package/src/utils/boundedFile.ts +116 -0
  385. package/src/utils/lockedJsonFile.ts +251 -0
  386. package/src/utils/platform.ts +31 -0
  387. package/src/utils/stableJson.ts +12 -0
  388. package/src/version.ts +3 -2
  389. package/src/watcherStatus.ts +19 -1
  390. package/ui/desktop-bootstrap.js +11 -0
  391. package/ui/desktop-bridge.js +36 -0
  392. package/ui/desktop.css +160 -0
  393. package/ui/getting-started.js +93 -0
  394. package/ui/i18n.js +484 -0
  395. package/ui/log-parser.js +30 -0
  396. package/ui/pages/audit.html +12 -0
  397. package/ui/pages/dashboard.html +94 -147
  398. package/ui/pages/direct-sessions.html +123 -26
  399. package/ui/pages/getting-started.html +43 -0
  400. package/ui/pages/logs.html +134 -22
  401. package/ui/pages/settings.html +101 -0
  402. package/ui/pages/task-detail.html +12 -0
  403. package/ui/pages/tasks.html +54 -8
  404. package/ui/pages/workspace.html +38 -1
  405. package/ui/settings.js +254 -0
  406. package/PatchWarden-Control.cmd +0 -6
  407. package/Restart-PatchWarden-Control.cmd +0 -6
  408. package/dist/test/unit/acceptance-engine.test.d.ts +0 -1
  409. package/dist/test/unit/acceptance-engine.test.js +0 -228
  410. package/dist/test/unit/agent-router.test.d.ts +0 -1
  411. package/dist/test/unit/agent-router.test.js +0 -287
  412. package/dist/test/unit/android-doctor.test.d.ts +0 -1
  413. package/dist/test/unit/android-doctor.test.js +0 -118
  414. package/dist/test/unit/apply-patch.test.d.ts +0 -1
  415. package/dist/test/unit/apply-patch.test.js +0 -225
  416. package/dist/test/unit/audit-checks.test.d.ts +0 -1
  417. package/dist/test/unit/audit-checks.test.js +0 -350
  418. package/dist/test/unit/chinese-path.test.d.ts +0 -1
  419. package/dist/test/unit/chinese-path.test.js +0 -91
  420. package/dist/test/unit/command-guard.test.d.ts +0 -1
  421. package/dist/test/unit/command-guard.test.js +0 -160
  422. package/dist/test/unit/create-task.test.d.ts +0 -1
  423. package/dist/test/unit/create-task.test.js +0 -197
  424. package/dist/test/unit/diagnose-task.test.d.ts +0 -1
  425. package/dist/test/unit/diagnose-task.test.js +0 -457
  426. package/dist/test/unit/direct-guards.test.d.ts +0 -1
  427. package/dist/test/unit/direct-guards.test.js +0 -328
  428. package/dist/test/unit/discovery-token-store.test.d.ts +0 -1
  429. package/dist/test/unit/discovery-token-store.test.js +0 -139
  430. package/dist/test/unit/evidence-pack.test.d.ts +0 -1
  431. package/dist/test/unit/evidence-pack.test.js +0 -224
  432. package/dist/test/unit/get-task-status.test.d.ts +0 -1
  433. package/dist/test/unit/get-task-status.test.js +0 -174
  434. package/dist/test/unit/get-task-summary.test.d.ts +0 -1
  435. package/dist/test/unit/get-task-summary.test.js +0 -146
  436. package/dist/test/unit/goal-graph.test.d.ts +0 -1
  437. package/dist/test/unit/goal-graph.test.js +0 -298
  438. package/dist/test/unit/goal-progress.test.d.ts +0 -1
  439. package/dist/test/unit/goal-progress.test.js +0 -381
  440. package/dist/test/unit/goal-report.test.d.ts +0 -1
  441. package/dist/test/unit/goal-report.test.js +0 -159
  442. package/dist/test/unit/goal-status.test.d.ts +0 -1
  443. package/dist/test/unit/goal-status.test.js +0 -215
  444. package/dist/test/unit/goal-store.test.d.ts +0 -1
  445. package/dist/test/unit/goal-store.test.js +0 -253
  446. package/dist/test/unit/goal-subgoal-task.test.d.ts +0 -1
  447. package/dist/test/unit/goal-subgoal-task.test.js +0 -55
  448. package/dist/test/unit/goal-tools-registry.test.d.ts +0 -1
  449. package/dist/test/unit/goal-tools-registry.test.js +0 -192
  450. package/dist/test/unit/handoff-export.test.d.ts +0 -1
  451. package/dist/test/unit/handoff-export.test.js +0 -263
  452. package/dist/test/unit/invoke-discovered-tool.test.d.ts +0 -1
  453. package/dist/test/unit/invoke-discovered-tool.test.js +0 -167
  454. package/dist/test/unit/logging.test.d.ts +0 -1
  455. package/dist/test/unit/logging.test.js +0 -397
  456. package/dist/test/unit/path-guard.test.d.ts +0 -1
  457. package/dist/test/unit/path-guard.test.js +0 -133
  458. package/dist/test/unit/post-task-cleanup.test.d.ts +0 -1
  459. package/dist/test/unit/post-task-cleanup.test.js +0 -48
  460. package/dist/test/unit/project-policy-release-mode.test.d.ts +0 -1
  461. package/dist/test/unit/project-policy-release-mode.test.js +0 -125
  462. package/dist/test/unit/reconcile-tasks.test.d.ts +0 -1
  463. package/dist/test/unit/reconcile-tasks.test.js +0 -456
  464. package/dist/test/unit/release-gate.test.d.ts +0 -1
  465. package/dist/test/unit/release-gate.test.js +0 -242
  466. package/dist/test/unit/run-task-loop.test.d.ts +0 -1
  467. package/dist/test/unit/run-task-loop.test.js +0 -380
  468. package/dist/test/unit/safe-status.test.d.ts +0 -1
  469. package/dist/test/unit/safe-status.test.js +0 -165
  470. package/dist/test/unit/safe-views.test.d.ts +0 -1
  471. package/dist/test/unit/safe-views.test.js +0 -171
  472. package/dist/test/unit/schema-drift-check.test.d.ts +0 -1
  473. package/dist/test/unit/schema-drift-check.test.js +0 -176
  474. package/dist/test/unit/sensitive-guard.test.d.ts +0 -1
  475. package/dist/test/unit/sensitive-guard.test.js +0 -104
  476. package/dist/test/unit/spec-kit-import.test.d.ts +0 -1
  477. package/dist/test/unit/spec-kit-import.test.js +0 -341
  478. package/dist/test/unit/subgoal-sync.test.d.ts +0 -1
  479. package/dist/test/unit/subgoal-sync.test.js +0 -183
  480. package/dist/test/unit/sync-file.test.d.ts +0 -1
  481. package/dist/test/unit/sync-file.test.js +0 -154
  482. package/dist/test/unit/tool-invocation-guard.test.d.ts +0 -1
  483. package/dist/test/unit/tool-invocation-guard.test.js +0 -432
  484. package/dist/test/unit/tool-usage-stats.test.d.ts +0 -1
  485. package/dist/test/unit/tool-usage-stats.test.js +0 -300
  486. package/dist/test/unit/toolSearch.test.d.ts +0 -1
  487. package/dist/test/unit/toolSearch.test.js +0 -571
  488. package/dist/test/unit/wait-for-task.test.d.ts +0 -1
  489. package/dist/test/unit/wait-for-task.test.js +0 -144
  490. package/dist/test/unit/watcher-status.test.d.ts +0 -1
  491. package/dist/test/unit/watcher-status.test.js +0 -169
  492. package/dist/test/unit/worktree-manager.test.d.ts +0 -1
  493. package/dist/test/unit/worktree-manager.test.js +0 -176
  494. package/dist/tools/applyPatch.js +0 -41
  495. package/dist/tools/cancelTask.d.ts +0 -30
  496. package/dist/tools/cancelTask.js +0 -64
  497. package/dist/tools/finalizeDirectSession.js +0 -84
  498. package/dist/tools/goalSubgoalTask.js +0 -110
  499. package/dist/tools/killTask.d.ts +0 -15
  500. package/dist/tools/listAgents.d.ts +0 -12
  501. package/dist/tools/listAgents.js +0 -42
  502. package/dist/tools/reconcileTasks.js +0 -352
  503. package/dist/tools/retryTask.js +0 -45
  504. package/dist/tools/runVerification.js +0 -36
  505. package/dist/tools/syncFile.js +0 -65
  506. package/docs/control-center/control-center-daily-driver.md +0 -211
  507. package/docs/control-center/control-center-mvp.md +0 -205
  508. package/docs/control-center/control-center-phase2.md +0 -159
  509. package/docs/execution-plan-2026-07-09.md +0 -315
  510. package/docs/release-v0.6.0.md +0 -71
  511. package/docs/release-v0.6.1.md +0 -75
  512. package/docs/release-v0.6.4.md +0 -45
  513. package/docs/roadmap-execution-and-acceptance.md +0 -365
  514. package/src/test/unit/acceptance-engine.test.ts +0 -261
  515. package/src/test/unit/agent-router.test.ts +0 -342
  516. package/src/test/unit/android-doctor.test.ts +0 -158
  517. package/src/test/unit/apply-patch.test.ts +0 -293
  518. package/src/test/unit/audit-checks.test.ts +0 -567
  519. package/src/test/unit/chinese-path.test.ts +0 -106
  520. package/src/test/unit/command-guard.test.ts +0 -221
  521. package/src/test/unit/create-task.test.ts +0 -255
  522. package/src/test/unit/diagnose-task.test.ts +0 -544
  523. package/src/test/unit/direct-guards.test.ts +0 -467
  524. package/src/test/unit/discovery-token-store.test.ts +0 -181
  525. package/src/test/unit/evidence-pack.test.ts +0 -251
  526. package/src/test/unit/get-task-status.test.ts +0 -203
  527. package/src/test/unit/get-task-summary.test.ts +0 -173
  528. package/src/test/unit/goal-graph.test.ts +0 -347
  529. package/src/test/unit/goal-progress.test.ts +0 -538
  530. package/src/test/unit/goal-report.test.ts +0 -189
  531. package/src/test/unit/goal-status.test.ts +0 -270
  532. package/src/test/unit/goal-store.test.ts +0 -318
  533. package/src/test/unit/goal-subgoal-task.test.ts +0 -72
  534. package/src/test/unit/goal-tools-registry.test.ts +0 -245
  535. package/src/test/unit/handoff-export.test.ts +0 -295
  536. package/src/test/unit/invoke-discovered-tool.test.ts +0 -216
  537. package/src/test/unit/logging.test.ts +0 -497
  538. package/src/test/unit/path-guard.test.ts +0 -180
  539. package/src/test/unit/post-task-cleanup.test.ts +0 -53
  540. package/src/test/unit/project-policy-release-mode.test.ts +0 -156
  541. package/src/test/unit/reconcile-tasks.test.ts +0 -551
  542. package/src/test/unit/release-gate.test.ts +0 -314
  543. package/src/test/unit/run-task-loop.test.ts +0 -425
  544. package/src/test/unit/safe-status.test.ts +0 -187
  545. package/src/test/unit/safe-views.test.ts +0 -184
  546. package/src/test/unit/schema-drift-check.test.ts +0 -260
  547. package/src/test/unit/sensitive-guard.test.ts +0 -124
  548. package/src/test/unit/spec-kit-import.test.ts +0 -429
  549. package/src/test/unit/subgoal-sync.test.ts +0 -236
  550. package/src/test/unit/sync-file.test.ts +0 -231
  551. package/src/test/unit/tool-invocation-guard.test.ts +0 -542
  552. package/src/test/unit/tool-usage-stats.test.ts +0 -384
  553. package/src/test/unit/toolSearch.test.ts +0 -756
  554. package/src/test/unit/wait-for-task.test.ts +0 -176
  555. package/src/test/unit/watcher-status.test.ts +0 -190
  556. package/src/test/unit/worktree-manager.test.ts +0 -247
  557. package/src/tools/cancelTask.ts +0 -70
  558. package/src/tools/listAgents.ts +0 -54
  559. package/src/tools/retryTask.ts +0 -53
  560. package/src/tools/syncFile.ts +0 -122
  561. /package/dist/tools/{toolCatalog.d.ts → catalog/toolCatalog.d.ts} +0 -0
  562. /package/dist/tools/{toolSearch.d.ts → catalog/toolSearch.d.ts} +0 -0
  563. /package/dist/tools/{toolSearch.js → catalog/toolSearch.js} +0 -0
  564. /package/dist/tools/{toolUsageStats.d.ts → catalog/toolUsageStats.d.ts} +0 -0
  565. /package/dist/tools/{toolUsageStats.js → catalog/toolUsageStats.js} +0 -0
  566. /package/dist/tools/{createDirectSession.d.ts → direct/createDirectSession.d.ts} +0 -0
  567. /package/dist/tools/{runDirectVerificationBundle.d.ts → direct/runDirectVerificationBundle.d.ts} +0 -0
  568. /package/dist/tools/{getPlan.d.ts → goals/getPlan.d.ts} +0 -0
  569. /package/dist/tools/{savePlan.d.ts → goals/savePlan.d.ts} +0 -0
  570. /package/dist/tools/{evidencePack.d.ts → tasks/evidencePack.d.ts} +0 -0
  571. /package/dist/tools/{getTaskFile.d.ts → tasks/getTaskFile.d.ts} +0 -0
  572. /package/dist/tools/{getTaskProgress.d.ts → tasks/getTaskProgress.d.ts} +0 -0
  573. /package/dist/tools/{getTaskProgress.js → tasks/getTaskProgress.js} +0 -0
  574. /package/dist/tools/{getTaskStdoutTail.d.ts → tasks/getTaskStdoutTail.d.ts} +0 -0
  575. /package/dist/tools/{getTaskSummary.d.ts → tasks/getTaskSummary.d.ts} +0 -0
  576. /package/dist/tools/{killTask.js → tasks/killTask.js} +0 -0
  577. /package/dist/tools/{retryTask.d.ts → tasks/retryTask.d.ts} +0 -0
  578. /package/dist/tools/{runVerification.d.ts → tasks/runVerification.d.ts} +0 -0
  579. /package/dist/tools/{taskLineage.d.ts → tasks/taskLineage.d.ts} +0 -0
  580. /package/dist/tools/{waitForTask.d.ts → tasks/waitForTask.d.ts} +0 -0
  581. /package/dist/tools/{androidDoctor.d.ts → workspace/androidDoctor.d.ts} +0 -0
  582. /package/dist/tools/{discardWorktree.d.ts → workspace/discardWorktree.d.ts} +0 -0
  583. /package/dist/tools/{listWorkspace.d.ts → workspace/listWorkspace.d.ts} +0 -0
  584. /package/dist/tools/{mergeWorktree.d.ts → workspace/mergeWorktree.d.ts} +0 -0
  585. /package/dist/tools/{readWorkspaceFile.d.ts → workspace/readWorkspaceFile.d.ts} +0 -0
  586. /package/dist/tools/{recommendAgentForTask.d.ts → workspace/recommendAgentForTask.d.ts} +0 -0
  587. /package/dist/tools/{searchWorkspace.d.ts → workspace/searchWorkspace.d.ts} +0 -0
  588. /package/src/tools/{toolSearch.ts → catalog/toolSearch.ts} +0 -0
  589. /package/src/tools/{toolUsageStats.ts → catalog/toolUsageStats.ts} +0 -0
  590. /package/src/tools/{getTaskProgress.ts → tasks/getTaskProgress.ts} +0 -0
  591. /package/src/tools/{killTask.ts → tasks/killTask.ts} +0 -0
@@ -1,14 +1,16 @@
1
- import { execSync } from "node:child_process";
2
- import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
1
+ import { execFileSync } from "node:child_process";
2
+ import { existsSync, mkdirSync, readdirSync, readFileSync } from "node:fs";
3
3
  import { join, resolve } from "node:path";
4
- import { getConfig } from "../config.js";
5
- import { guardReadPath, guardWorkspacePath } from "../security/pathGuard.js";
6
- import { countRedactionsByCategory, redactSensitiveValue } from "../security/contentRedaction.js";
7
- import { getProjectPolicySummary } from "../policy/projectPolicy.js";
4
+ import { getConfig } from "../../config.js";
5
+ import { guardReadPath, guardWorkspacePath } from "../../security/pathGuard.js";
6
+ import { countRedactionsByCategory, redactSensitiveValue } from "../../security/contentRedaction.js";
7
+ import { getProjectPolicySummary } from "../../policy/projectPolicy.js";
8
8
  import { getTaskLineage } from "./taskLineage.js";
9
- import { getLastToolCatalogSnapshot } from "./toolCatalog.js";
10
- import { PATCHWARDEN_VERSION, TOOL_SCHEMA_EPOCH } from "../version.js";
11
- import { PatchWardenError } from "../errors.js";
9
+ import { getLastToolCatalogSnapshot } from "../catalog/toolCatalog.js";
10
+ import { PATCHWARDEN_VERSION, TOOL_SCHEMA_EPOCH } from "../../version.js";
11
+ import { PatchWardenError } from "../../errors.js";
12
+ import { buildGitEnvironment, resolveTrustedExecutable } from "../../runner/processSecurity.js";
13
+ import { atomicWriteFileSync, atomicWriteJsonFileSync } from "../../utils/atomicFile.js";
12
14
  export function exportTaskEvidencePack(input) {
13
15
  const lineageId = normalizeLineageId(input.lineage_id);
14
16
  const maxItems = normalizeMaxItems(input.max_items);
@@ -35,7 +37,7 @@ export function exportTaskEvidencePack(input) {
35
37
  const verifyContent = buildVerifyContent(lineage);
36
38
  const diffstatContent = buildDiffstatContent(lineage, config);
37
39
  const lineageSummary = buildLineageSummary(lineage);
38
- const attestationContent = buildAttestation(catalog, generatedAt);
40
+ const attestationContent = buildAttestation(catalog, generatedAt, lineage.repo_path, config.workspaceRoot);
39
41
  // Count redactions across all v2 raw contents for the audit trail.
40
42
  const v2RawPayloads = [
41
43
  riskContent,
@@ -62,12 +64,12 @@ export function exportTaskEvidencePack(input) {
62
64
  note: "Only categories and counts are recorded; original secret values are never persisted.",
63
65
  };
64
66
  // Redact and write each v2 file.
65
- writeFileSync(filePaths.risk, JSON.stringify(redactSensitiveValue(riskContent).value, null, 2) + "\n", "utf-8");
66
- writeFileSync(filePaths.verify, JSON.stringify(redactSensitiveValue(verifyContent).value, null, 2) + "\n", "utf-8");
67
- writeFileSync(filePaths.diffstat, JSON.stringify(redactSensitiveValue(diffstatContent).value, null, 2) + "\n", "utf-8");
68
- writeFileSync(filePaths.lineage, JSON.stringify(redactSensitiveValue(lineageSummary).value, null, 2) + "\n", "utf-8");
69
- writeFileSync(filePaths.attestation, JSON.stringify(redactSensitiveValue(attestationContent).value, null, 2) + "\n", "utf-8");
70
- writeFileSync(filePaths.redactions, JSON.stringify(redactSensitiveValue(redactionsContent).value, null, 2) + "\n", "utf-8");
67
+ atomicWriteJsonFileSync(filePaths.risk, redactSensitiveValue(riskContent).value);
68
+ atomicWriteJsonFileSync(filePaths.verify, redactSensitiveValue(verifyContent).value);
69
+ atomicWriteJsonFileSync(filePaths.diffstat, redactSensitiveValue(diffstatContent).value);
70
+ atomicWriteJsonFileSync(filePaths.lineage, redactSensitiveValue(lineageSummary).value);
71
+ atomicWriteJsonFileSync(filePaths.attestation, redactSensitiveValue(attestationContent).value);
72
+ atomicWriteJsonFileSync(filePaths.redactions, redactSensitiveValue(redactionsContent).value);
71
73
  const safePack = redactSensitiveValue({
72
74
  evidence_pack_id: `evidence_${lineageId}`,
73
75
  lineage_id: lineageId,
@@ -94,8 +96,8 @@ export function exportTaskEvidencePack(input) {
94
96
  next_action: buildNextAction(lineage),
95
97
  bounded: true,
96
98
  }).value;
97
- writeFileSync(safePack.files.json, JSON.stringify(safePack, null, 2) + "\n", "utf-8");
98
- writeFileSync(safePack.files.markdown, buildEvidenceMarkdown(safePack), "utf-8");
99
+ atomicWriteJsonFileSync(safePack.files.json, safePack);
100
+ atomicWriteFileSync(safePack.files.markdown, buildEvidenceMarkdown(safePack));
99
101
  return safePack;
100
102
  }
101
103
  export function listEvidencePacks(options = {}) {
@@ -308,19 +310,29 @@ function buildLineageSummary(lineage) {
308
310
  truncated: lineage.truncated,
309
311
  };
310
312
  }
311
- function readGitCommitShort() {
313
+ function readGitCommitShort(repoPath, workspaceRoot) {
312
314
  try {
313
- return execSync("git rev-parse --short HEAD", { encoding: "utf-8", timeout: 5000 }).trim();
315
+ const cwd = guardWorkspacePath(repoPath || ".", workspaceRoot);
316
+ const env = buildGitEnvironment(cwd);
317
+ const git = resolveTrustedExecutable("git", cwd, { pathValue: env.PATH });
318
+ return execFileSync(git, ["rev-parse", "--short", "HEAD"], {
319
+ cwd,
320
+ encoding: "utf-8",
321
+ env,
322
+ stdio: ["ignore", "pipe", "ignore"],
323
+ timeout: 5000,
324
+ windowsHide: true,
325
+ }).trim();
314
326
  }
315
327
  catch {
316
328
  return "unknown";
317
329
  }
318
330
  }
319
- function buildAttestation(catalog, generatedAt) {
331
+ function buildAttestation(catalog, generatedAt, repoPath, workspaceRoot) {
320
332
  return {
321
333
  patchwarden_version: PATCHWARDEN_VERSION,
322
334
  package_version: PATCHWARDEN_VERSION,
323
- commit: readGitCommitShort(),
335
+ commit: readGitCommitShort(repoPath, workspaceRoot),
324
336
  node_version: process.version,
325
337
  os: { platform: process.platform, arch: process.arch },
326
338
  tool_profile: catalog.tool_profile,
@@ -1,9 +1,9 @@
1
1
  import { readFileSync, existsSync, statSync } from "node:fs";
2
2
  import { resolve, join } from "node:path";
3
- import { getTasksDir, getConfig } from "../config.js";
4
- import { guardPath } from "../security/pathGuard.js";
5
- import { guardSensitivePath } from "../security/sensitiveGuard.js";
6
- import { redactSensitiveContent } from "../security/contentRedaction.js";
3
+ import { getTasksDir, getConfig } from "../../config.js";
4
+ import { guardPath } from "../../security/pathGuard.js";
5
+ import { guardSensitivePath } from "../../security/sensitiveGuard.js";
6
+ import { redactSensitiveContent } from "../../security/contentRedaction.js";
7
7
  import { getTaskStatus } from "./getTaskStatus.js";
8
8
  export const TASK_READ_ONLY_FILES = [
9
9
  "result.md",
@@ -1,6 +1,6 @@
1
1
  import type { TaskStatus } from "./createTask.js";
2
2
  import type { TaskPhase } from "./createTask.js";
3
- import { type PendingReason, type WatcherStatusSnapshot } from "../watcherStatus.js";
3
+ import { type PendingReason, type WatcherStatusSnapshot } from "../../watcherStatus.js";
4
4
  export interface GetTaskStatusOutput {
5
5
  task_id: string;
6
6
  plan_id: string;
@@ -28,6 +28,11 @@ export interface GetTaskStatusOutput {
28
28
  path: string;
29
29
  change: string;
30
30
  }>;
31
+ new_out_of_scope_changes?: Array<{
32
+ path: string;
33
+ change: string;
34
+ }>;
35
+ artifact_hygiene_counts?: Record<string, unknown>;
31
36
  verify_status?: "passed" | "failed" | "skipped";
32
37
  verify_commands?: string[];
33
38
  diff_available?: boolean;
@@ -1,10 +1,11 @@
1
1
  import { readFileSync, existsSync } from "node:fs";
2
2
  import { resolve, join } from "node:path";
3
- import { getTasksDir, getConfig } from "../config.js";
4
- import { guardReadPath } from "../security/pathGuard.js";
5
- import { guardSensitivePath } from "../security/sensitiveGuard.js";
6
- import { readTaskRuntime } from "../taskRuntime.js";
7
- import { derivePendingReason, readWatcherStatus, } from "../watcherStatus.js";
3
+ import { getTasksDir, getConfig } from "../../config.js";
4
+ import { guardReadPath } from "../../security/pathGuard.js";
5
+ import { guardSensitivePath } from "../../security/sensitiveGuard.js";
6
+ import { readTaskRuntime } from "../../runner/taskRuntime.js";
7
+ import { derivePendingReason, readWatcherStatus, } from "../../watcherStatus.js";
8
+ import { isTerminalTaskStatus } from "./taskStates.js";
8
9
  export function getTaskStatus(taskId) {
9
10
  const config = getConfig();
10
11
  const tasksDir = getTasksDir(config);
@@ -18,7 +19,11 @@ export function getTaskStatus(taskId) {
18
19
  const raw = readFileSync(statusFile, "utf-8");
19
20
  const status = JSON.parse(raw);
20
21
  const runtime = readTaskRuntime(taskDir);
21
- const phase = runtime.phase || status.phase || "queued";
22
+ // A terminal status is authoritative. A stale runtime phase from a crashed
23
+ // Runner must never make a reconciled task look active again.
24
+ const phase = isTerminalTaskStatus(String(status.status))
25
+ ? status.phase || "queued"
26
+ : runtime.phase || status.phase || "queued";
22
27
  const watcher = readWatcherStatus(config);
23
28
  const pendingReason = derivePendingReason({ status: status.status, phase }, watcher);
24
29
  return {
@@ -1,8 +1,9 @@
1
- import { readFileSync, existsSync } from "node:fs";
1
+ import { existsSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { getTasksDir, getConfig } from "../config.js";
4
- import { guardReadPath } from "../security/pathGuard.js";
5
- import { redactSensitiveContent } from "../security/contentRedaction.js";
3
+ import { getTasksDir, getConfig } from "../../config.js";
4
+ import { guardReadPath } from "../../security/pathGuard.js";
5
+ import { redactSensitiveContent } from "../../security/contentRedaction.js";
6
+ import { readTextFilePrefixSync, readTextFileTailLinesSync } from "../../utils/boundedFile.js";
6
7
  export function getTaskStdoutTail(taskId, lines = 80) {
7
8
  const config = getConfig();
8
9
  const tasksDir = getTasksDir(config);
@@ -16,29 +17,30 @@ export function getTaskStdoutTail(taskId, lines = 80) {
16
17
  const stdoutLog = join(taskDir, "stdout.log");
17
18
  const stderrLog = join(taskDir, "stderr.log");
18
19
  const resultFile = join(taskDir, "result.md");
19
- const maxLines = Math.min(lines, 200);
20
+ const maxLines = Number.isFinite(lines)
21
+ ? Math.max(1, Math.min(200, Math.trunc(lines)))
22
+ : 80;
23
+ const stderrLines = Math.min(maxLines, 40);
20
24
  let stdoutTail = "";
21
25
  let stderrTail = "";
22
26
  let source = "none";
23
27
  // All log files are optional — don't throw if missing
24
28
  if (existsSync(stdoutLog)) {
25
- const raw = readFileSync(stdoutLog, "utf-8");
26
- stdoutTail = raw.split("\n").slice(-maxLines).join("\n");
29
+ stdoutTail = readTextFileTailLinesSync(stdoutLog, maxLines, config.maxReadFileBytes);
27
30
  source = "stdout.log";
28
31
  }
29
32
  if (existsSync(stderrLog)) {
30
- const raw = readFileSync(stderrLog, "utf-8");
31
- stderrTail = raw.split("\n").slice(-Math.min(maxLines, 40)).join("\n");
33
+ stderrTail = readTextFileTailLinesSync(stderrLog, stderrLines, config.maxReadFileBytes);
32
34
  }
33
35
  // Fallback to result.md
34
36
  if (!stdoutTail && existsSync(resultFile)) {
35
- const raw = readFileSync(resultFile, "utf-8");
37
+ const raw = readTextFilePrefixSync(resultFile, config.maxReadFileBytes).content;
36
38
  const stdoutMatch = raw.match(/## Agent stdout\s*\n+```\s*([\s\S]*?)```/i);
37
39
  const stderrMatch = raw.match(/## Agent stderr\s*\n+```\s*([\s\S]*?)```/i);
38
40
  if (stdoutMatch)
39
41
  stdoutTail = stdoutMatch[1].split("\n").slice(-maxLines).join("\n");
40
42
  if (stderrMatch && !stderrTail)
41
- stderrTail = stderrMatch[1].split("\n").slice(-Math.min(maxLines, 40)).join("\n");
43
+ stderrTail = stderrMatch[1].split("\n").slice(-stderrLines).join("\n");
42
44
  if (source === "none")
43
45
  source = "result.md";
44
46
  }
@@ -1,18 +1,11 @@
1
- import { existsSync, readFileSync } from "node:fs";
1
+ import { existsSync } from "node:fs";
2
2
  import { join, resolve } from "node:path";
3
- import { getConfig, getTasksDir } from "../config.js";
4
- import { guardReadPath } from "../security/pathGuard.js";
3
+ import { getConfig, getTasksDir } from "../../config.js";
4
+ import { guardReadPath } from "../../security/pathGuard.js";
5
5
  import { getTaskStatus } from "./getTaskStatus.js";
6
- import { redactSensitiveValue } from "../security/contentRedaction.js";
7
- const TERMINAL_STATUSES = new Set([
8
- "done",
9
- "done_by_agent",
10
- "failed",
11
- "failed_verification",
12
- "failed_scope_violation",
13
- "failed_policy_violation",
14
- "canceled",
15
- ]);
6
+ import { redactSensitiveValue } from "../../security/contentRedaction.js";
7
+ import { readTextFilePrefixSync, readTextFileTailLinesSync, } from "../../utils/boundedFile.js";
8
+ import { isTerminalTaskStatus } from "./taskStates.js";
16
9
  export function getTaskSummary(taskId, options = {}) {
17
10
  const config = getConfig();
18
11
  const taskDir = resolve(getTasksDir(config), taskId);
@@ -23,14 +16,15 @@ export function getTaskSummary(taskId, options = {}) {
23
16
  const verifyRead = tryReadJson(join(taskDir, "verify.json"));
24
17
  const result = resultRead.data;
25
18
  const verify = verifyRead.data;
26
- const terminal = TERMINAL_STATUSES.has(String(status.status));
19
+ const terminal = isTerminalTaskStatus(String(status.status));
27
20
  // Phase 4: Use new_out_of_scope_changes (task-caused) for acceptance status.
28
21
  // Pre-existing external dirty files that didn't change should NOT fail acceptance.
29
22
  const outOfScope = asArray(result.new_out_of_scope_changes
30
23
  ?? status.new_out_of_scope_changes
31
24
  ?? result.out_of_scope_changes
32
25
  ?? status.out_of_scope_changes);
33
- const verifyStatus = String(verify.status ?? result.verify_status ?? result.verify?.status ?? status.verify_status ?? "not_available");
26
+ const resultVerify = asRecord(result.verify);
27
+ const verifyStatus = String(verify.status ?? result.verify_status ?? resultVerify.status ?? status.verify_status ?? "not_available");
34
28
  const errors = [status.error, ...asArray(result.errors), ...asArray(result.known_issues)]
35
29
  .filter((value) => typeof value === "string" && value.trim() !== "");
36
30
  const warnings = asArray(result.warnings).filter((value) => typeof value === "string");
@@ -61,16 +55,16 @@ export function getTaskSummary(taskId, options = {}) {
61
55
  const acceptanceFile = join(taskDir, "acceptance.json");
62
56
  if (existsSync(acceptanceFile)) {
63
57
  try {
64
- const acceptance = JSON.parse(readFileSync(acceptanceFile, "utf-8"));
58
+ const acceptance = asRecord(JSON.parse(readTextFilePrefixSync(acceptanceFile, 1024 * 1024).content));
65
59
  if (acceptance.status === "accepted") {
66
60
  acceptanceStatus = "ready_for_review";
67
- acceptanceReviewedAt = acceptance.reviewed_at || null;
68
- acceptanceReviewer = acceptance.reviewer || null;
61
+ acceptanceReviewedAt = typeof acceptance.reviewed_at === "string" ? acceptance.reviewed_at : null;
62
+ acceptanceReviewer = typeof acceptance.reviewer === "string" ? acceptance.reviewer : null;
69
63
  }
70
64
  else if (acceptance.status === "rejected") {
71
65
  acceptanceStatus = "failed";
72
- acceptanceReviewedAt = acceptance.reviewed_at || null;
73
- acceptanceReviewer = acceptance.reviewer || null;
66
+ acceptanceReviewedAt = typeof acceptance.reviewed_at === "string" ? acceptance.reviewed_at : null;
67
+ acceptanceReviewer = typeof acceptance.reviewer === "string" ? acceptance.reviewer : null;
74
68
  if (acceptance.notes) {
75
69
  warnings.push(`Task was rejected by ${acceptance.reviewer || "human"} at ${acceptance.reviewed_at || "unknown time"}: ${acceptance.notes}`);
76
70
  }
@@ -102,11 +96,13 @@ export function getTaskSummary(taskId, options = {}) {
102
96
  const artifactHygiene = asRecord(result.artifact_hygiene ?? changedFilesRead.data.artifact_hygiene ?? {
103
97
  counts: status.artifact_hygiene_counts || {},
104
98
  });
105
- const verifyCommands = asArray(verify.commands ?? result.verify_commands ?? result.verify?.commands);
99
+ const verifyCommands = asArray(verify.commands ?? result.verify_commands ?? resultVerify.commands);
106
100
  const testLogSummary = summarizeTestLog(join(taskDir, "test.log"));
107
101
  const verificationSummary = buildVerificationSummary(verifyStatus, verifyCommands, testLogSummary);
108
102
  // Extract failed command detail from verify records
109
- const failedVerify = verifyCommands.find((cmd) => ["failed", "timed_out", "canceled"].includes(cmd?.status));
103
+ const failedVerify = verifyCommands
104
+ .map(asRecord)
105
+ .find((command) => ["failed", "timed_out", "canceled"].includes(String(command.status || "")));
110
106
  const failedCommandDetail = failedVerify ? {
111
107
  command: String(failedVerify.command || ""),
112
108
  exit_code: failedVerify.exit_code ?? null,
@@ -145,7 +141,7 @@ export function getTaskSummary(taskId, options = {}) {
145
141
  test_summary: verificationSummary.headline,
146
142
  diff_available: Boolean((status.diff_available ?? (changedFiles.length > 0)) &&
147
143
  (artifacts["diff.patch"] || artifacts["git.diff"])),
148
- diff_truncated: Boolean(status.diff_truncated || result.warnings?.some?.((warning) => warning.includes("diff.patch was truncated"))),
144
+ diff_truncated: Boolean(status.diff_truncated || asArray(result.warnings).some((warning) => typeof warning === "string" && warning.includes("diff.patch was truncated"))),
149
145
  result_available: artifacts["result.md"],
150
146
  result_json_available: artifacts["result.json"],
151
147
  verify_available: artifacts["verify.json"],
@@ -194,7 +190,9 @@ function buildCompactSummary(output, maxItems) {
194
190
  const manifestRead = tryReadJson(join(taskDir, "artifact_manifest.json"));
195
191
  const manifest = asRecord(manifestRead.data);
196
192
  const releaseArtifacts = asArray(manifest.artifacts);
197
- const releaseArtifactPaths = releaseArtifacts.map((a) => String(a.path || "")).slice(0, maxItems);
193
+ const releaseArtifactPaths = releaseArtifacts
194
+ .map((artifact) => String(asRecord(artifact).path || ""))
195
+ .slice(0, maxItems);
198
196
  const compact = {
199
197
  view: "compact",
200
198
  task_id: String(output.task_id),
@@ -243,7 +241,7 @@ function tryReadJson(path) {
243
241
  if (!existsSync(path))
244
242
  return { data: {} };
245
243
  try {
246
- return { data: JSON.parse(readFileSync(path, "utf-8")) };
244
+ return { data: asRecord(JSON.parse(readTextFilePrefixSync(path, 2 * 1024 * 1024).content)) };
247
245
  }
248
246
  catch (error) {
249
247
  return { data: {}, error: error instanceof Error ? error.message : String(error) };
@@ -253,27 +251,30 @@ function asArray(value) {
253
251
  return Array.isArray(value) ? value : [];
254
252
  }
255
253
  function asRecord(value) {
256
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
254
+ return value && typeof value === "object" && !Array.isArray(value)
255
+ ? value
256
+ : {};
257
257
  }
258
258
  function summarizeTestLog(path) {
259
259
  if (!existsSync(path))
260
260
  return "test.log missing";
261
- const text = readFileSync(path, "utf-8");
261
+ const text = readTextFileTailLinesSync(path, 200, 512 * 1024);
262
262
  const exit = text.match(/Exit\s*code:\s*([^\r\n]+)/i)?.[1]?.trim();
263
263
  return exit ? `Exit code: ${exit}` : text.trim().slice(0, 500) || "test.log empty";
264
264
  }
265
265
  function readResultFallback(path) {
266
266
  if (!existsSync(path))
267
267
  return "";
268
- const text = readFileSync(path, "utf-8");
268
+ const text = readTextFilePrefixSync(path, 1024 * 1024).content;
269
269
  return text.match(/## Summary\s+([\s\S]*?)(?:\n## |\n---|$)/i)?.[1]?.trim().slice(0, 1000) || "";
270
270
  }
271
271
  function buildVerificationSummary(status, commands, testLogSummary) {
272
- const passed = commands.filter((command) => command?.status === "passed").length;
273
- const failed = commands.filter((command) => ["failed", "timed_out", "canceled"].includes(command?.status)).length;
274
- const skipped = commands.filter((command) => command?.status === "skipped").length;
272
+ const commandRecords = commands.map(asRecord);
273
+ const passed = commandRecords.filter((command) => command.status === "passed").length;
274
+ const failed = commandRecords.filter((command) => ["failed", "timed_out", "canceled"].includes(String(command.status || ""))).length;
275
+ const skipped = commandRecords.filter((command) => command.status === "skipped").length;
275
276
  const evidenceText = [
276
- ...commands.flatMap((command) => [command?.stdout_tail, command?.stderr_tail]),
277
+ ...commandRecords.flatMap((command) => [command.stdout_tail, command.stderr_tail]),
277
278
  testLogSummary,
278
279
  ].filter((value) => typeof value === "string").join("\n");
279
280
  const headline = extractTestHeadline(evidenceText)
@@ -304,10 +305,8 @@ function readLogTail(path, lines) {
304
305
  if (!existsSync(path))
305
306
  return "(file not found)";
306
307
  try {
307
- const raw = readFileSync(path, "utf-8");
308
- if (raw.length === 0)
309
- return "(empty)";
310
- return raw.split("\n").slice(-lines).join("\n");
308
+ const tail = readTextFileTailLinesSync(path, lines);
309
+ return tail || "(empty)";
311
310
  }
312
311
  catch {
313
312
  return "(unreadable)";
@@ -0,0 +1,18 @@
1
+ export * from "./cancelTask.js";
2
+ export * from "./createTask.js";
3
+ export * from "./diagnoseTask.js";
4
+ export * from "./evidencePack.js";
5
+ export * from "./getTaskFile.js";
6
+ export * from "./getTaskProgress.js";
7
+ export * from "./getTaskStatus.js";
8
+ export * from "./getTaskStdoutTail.js";
9
+ export * from "./getTaskSummary.js";
10
+ export * from "./killTask.js";
11
+ export * from "./listTasks.js";
12
+ export * from "./reconcileTasks.js";
13
+ export * from "./retryTask.js";
14
+ export * from "./runTaskLoop.js";
15
+ export * from "./runVerification.js";
16
+ export * from "./taskLineage.js";
17
+ export * from "./taskOutputs.js";
18
+ export * from "./waitForTask.js";
@@ -0,0 +1,18 @@
1
+ export * from "./cancelTask.js";
2
+ export * from "./createTask.js";
3
+ export * from "./diagnoseTask.js";
4
+ export * from "./evidencePack.js";
5
+ export * from "./getTaskFile.js";
6
+ export * from "./getTaskProgress.js";
7
+ export * from "./getTaskStatus.js";
8
+ export * from "./getTaskStdoutTail.js";
9
+ export * from "./getTaskSummary.js";
10
+ export * from "./killTask.js";
11
+ export * from "./listTasks.js";
12
+ export * from "./reconcileTasks.js";
13
+ export * from "./retryTask.js";
14
+ export * from "./runTaskLoop.js";
15
+ export * from "./runVerification.js";
16
+ export * from "./taskLineage.js";
17
+ export * from "./taskOutputs.js";
18
+ export * from "./waitForTask.js";
@@ -0,0 +1 @@
1
+ export declare function killTask(taskId: string): import("./cancelTask.js").TaskTerminationResponse;
@@ -1,5 +1,5 @@
1
1
  import type { TaskPhase, TaskStatus, AcceptanceStatus } from "./createTask.js";
2
- import { type PendingReason, type WatcherState, type WatcherStatusSnapshot } from "../watcherStatus.js";
2
+ import { type PendingReason, type WatcherState, type WatcherStatusSnapshot } from "../../watcherStatus.js";
3
3
  export interface TaskEntry {
4
4
  task_id: string;
5
5
  plan_id: string;
@@ -1,8 +1,9 @@
1
1
  import { readdirSync, readFileSync, existsSync, statSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { getTasksDir, getPlansDir, getConfig } from "../config.js";
4
- import { readTaskRuntime } from "../taskRuntime.js";
5
- import { derivePendingReason, readWatcherStatus, } from "../watcherStatus.js";
3
+ import { getTasksDir, getPlansDir, getConfig } from "../../config.js";
4
+ import { readTaskRuntime } from "../../runner/taskRuntime.js";
5
+ import { derivePendingReason, readWatcherStatus, } from "../../watcherStatus.js";
6
+ import { isActiveTaskStatus } from "./taskStates.js";
6
7
  export function listTasks(input) {
7
8
  const config = getConfig();
8
9
  const tasksDir = getTasksDir(config);
@@ -48,7 +49,7 @@ export function listTasks(input) {
48
49
  if (taskAcceptance !== filterAcceptance)
49
50
  continue;
50
51
  }
51
- if (input?.active_only && !["pending", "running", "collecting_artifacts"].includes(String(data.status)))
52
+ if (input?.active_only && !isActiveTaskStatus(String(data.status)))
52
53
  continue;
53
54
  const normalizedRepo = String(data.repo_path || ".").replace(/\\/g, "/");
54
55
  const normalizedResolvedRepo = String(data.resolved_repo_path || "").replace(/\\/g, "/");
@@ -1,10 +1,11 @@
1
- import { type PatchWardenConfig } from "../config.js";
1
+ import { type PatchWardenConfig } from "../../config.js";
2
2
  import { type DiagnosisType, type DiagnosisConfidence, type SafeAction } from "./diagnoseTask.js";
3
3
  export type ReconcileMode = "report_only" | "safe_fix";
4
4
  export interface ReconcileTasksInput {
5
5
  max_age_minutes?: number;
6
6
  mode?: ReconcileMode;
7
7
  include_done_candidates?: boolean;
8
+ task_ids?: string[];
8
9
  }
9
10
  export interface ReconcileTaskReport {
10
11
  task_id: string;
@@ -15,7 +16,7 @@ export interface ReconcileTaskReport {
15
16
  reasons: string[];
16
17
  safe_actions: SafeAction[];
17
18
  age_seconds: number | null;
18
- action_taken: "left_unchanged" | "marked_failed_stale" | "marked_orphaned" | "marked_done_by_agent";
19
+ action_taken: "left_unchanged" | "marked_failed_stale" | "marked_orphaned" | "marked_done_by_agent" | "marked_canceled" | "marked_timed_out";
19
20
  previous_status: string | null;
20
21
  new_status: string | null;
21
22
  applied_at: string | null;