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, type SafeTaskLineage } 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
 
13
15
  export interface SafeEvidencePack {
14
16
  evidence_pack_id: string;
@@ -72,7 +74,7 @@ export function exportTaskEvidencePack(input: { lineage_id: string; max_items?:
72
74
  const verifyContent = buildVerifyContent(lineage);
73
75
  const diffstatContent = buildDiffstatContent(lineage, config);
74
76
  const lineageSummary = buildLineageSummary(lineage);
75
- const attestationContent = buildAttestation(catalog, generatedAt);
77
+ const attestationContent = buildAttestation(catalog, generatedAt, lineage.repo_path, config.workspaceRoot);
76
78
 
77
79
  // Count redactions across all v2 raw contents for the audit trail.
78
80
  const v2RawPayloads = [
@@ -99,12 +101,12 @@ export function exportTaskEvidencePack(input: { lineage_id: string; max_items?:
99
101
  };
100
102
 
101
103
  // Redact and write each v2 file.
102
- writeFileSync(filePaths.risk, JSON.stringify(redactSensitiveValue(riskContent).value, null, 2) + "\n", "utf-8");
103
- writeFileSync(filePaths.verify, JSON.stringify(redactSensitiveValue(verifyContent).value, null, 2) + "\n", "utf-8");
104
- writeFileSync(filePaths.diffstat, JSON.stringify(redactSensitiveValue(diffstatContent).value, null, 2) + "\n", "utf-8");
105
- writeFileSync(filePaths.lineage, JSON.stringify(redactSensitiveValue(lineageSummary).value, null, 2) + "\n", "utf-8");
106
- writeFileSync(filePaths.attestation, JSON.stringify(redactSensitiveValue(attestationContent).value, null, 2) + "\n", "utf-8");
107
- writeFileSync(filePaths.redactions, JSON.stringify(redactSensitiveValue(redactionsContent).value, null, 2) + "\n", "utf-8");
104
+ atomicWriteJsonFileSync(filePaths.risk, redactSensitiveValue(riskContent).value);
105
+ atomicWriteJsonFileSync(filePaths.verify, redactSensitiveValue(verifyContent).value);
106
+ atomicWriteJsonFileSync(filePaths.diffstat, redactSensitiveValue(diffstatContent).value);
107
+ atomicWriteJsonFileSync(filePaths.lineage, redactSensitiveValue(lineageSummary).value);
108
+ atomicWriteJsonFileSync(filePaths.attestation, redactSensitiveValue(attestationContent).value);
109
+ atomicWriteJsonFileSync(filePaths.redactions, redactSensitiveValue(redactionsContent).value);
108
110
 
109
111
  const safePack = redactSensitiveValue({
110
112
  evidence_pack_id: `evidence_${lineageId}`,
@@ -133,8 +135,8 @@ export function exportTaskEvidencePack(input: { lineage_id: string; max_items?:
133
135
  bounded: true,
134
136
  }).value as SafeEvidencePack;
135
137
 
136
- writeFileSync(safePack.files.json, JSON.stringify(safePack, null, 2) + "\n", "utf-8");
137
- writeFileSync(safePack.files.markdown, buildEvidenceMarkdown(safePack), "utf-8");
138
+ atomicWriteJsonFileSync(safePack.files.json, safePack);
139
+ atomicWriteFileSync(safePack.files.markdown, buildEvidenceMarkdown(safePack));
138
140
  return safePack;
139
141
  }
140
142
 
@@ -410,9 +412,19 @@ function buildLineageSummary(lineage: SafeTaskLineage): {
410
412
  };
411
413
  }
412
414
 
413
- function readGitCommitShort(): string {
415
+ function readGitCommitShort(repoPath: string, workspaceRoot: string): string {
414
416
  try {
415
- return execSync("git rev-parse --short HEAD", { encoding: "utf-8", timeout: 5000 }).trim();
417
+ const cwd = guardWorkspacePath(repoPath || ".", workspaceRoot);
418
+ const env = buildGitEnvironment(cwd);
419
+ const git = resolveTrustedExecutable("git", cwd, { pathValue: env.PATH });
420
+ return execFileSync(git, ["rev-parse", "--short", "HEAD"], {
421
+ cwd,
422
+ encoding: "utf-8",
423
+ env,
424
+ stdio: ["ignore", "pipe", "ignore"],
425
+ timeout: 5000,
426
+ windowsHide: true,
427
+ }).trim();
416
428
  } catch {
417
429
  return "unknown";
418
430
  }
@@ -420,7 +432,9 @@ function readGitCommitShort(): string {
420
432
 
421
433
  function buildAttestation(
422
434
  catalog: SafeEvidencePack["catalog"],
423
- generatedAt: string
435
+ generatedAt: string,
436
+ repoPath: string,
437
+ workspaceRoot: string,
424
438
  ): {
425
439
  patchwarden_version: string;
426
440
  package_version: string;
@@ -434,7 +448,7 @@ function buildAttestation(
434
448
  return {
435
449
  patchwarden_version: PATCHWARDEN_VERSION,
436
450
  package_version: PATCHWARDEN_VERSION,
437
- commit: readGitCommitShort(),
451
+ commit: readGitCommitShort(repoPath, workspaceRoot),
438
452
  node_version: process.version,
439
453
  os: { platform: process.platform, arch: process.arch },
440
454
  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
 
9
9
  export interface GetTaskFileOutput {
@@ -1,17 +1,18 @@
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";
3
+ import { getTasksDir, getConfig } from "../../config.js";
4
+ import { guardReadPath } from "../../security/pathGuard.js";
5
+ import { guardSensitivePath } from "../../security/sensitiveGuard.js";
6
6
  import type { TaskStatus } from "./createTask.js";
7
7
  import type { TaskPhase } from "./createTask.js";
8
- import { readTaskRuntime } from "../taskRuntime.js";
8
+ import { readTaskRuntime } from "../../runner/taskRuntime.js";
9
9
  import {
10
10
  derivePendingReason,
11
11
  readWatcherStatus,
12
12
  type PendingReason,
13
13
  type WatcherStatusSnapshot,
14
- } from "../watcherStatus.js";
14
+ } from "../../watcherStatus.js";
15
+ import { isTerminalTaskStatus } from "./taskStates.js";
15
16
 
16
17
  export interface GetTaskStatusOutput {
17
18
  task_id: string;
@@ -34,6 +35,8 @@ export interface GetTaskStatusOutput {
34
35
  finished_at?: string;
35
36
  changed_files?: Array<{ path: string; change: string }>;
36
37
  out_of_scope_changes?: Array<{ path: string; change: string }>;
38
+ new_out_of_scope_changes?: Array<{ path: string; change: string }>;
39
+ artifact_hygiene_counts?: Record<string, unknown>;
37
40
  verify_status?: "passed" | "failed" | "skipped";
38
41
  verify_commands?: string[];
39
42
  diff_available?: boolean;
@@ -67,7 +70,11 @@ export function getTaskStatus(taskId: string): GetTaskStatusOutput {
67
70
  const raw = readFileSync(statusFile, "utf-8");
68
71
  const status = JSON.parse(raw) as GetTaskStatusOutput;
69
72
  const runtime = readTaskRuntime(taskDir);
70
- const phase = runtime.phase || status.phase || "queued";
73
+ // A terminal status is authoritative. A stale runtime phase from a crashed
74
+ // Runner must never make a reconciled task look active again.
75
+ const phase = isTerminalTaskStatus(String(status.status))
76
+ ? status.phase || "queued"
77
+ : runtime.phase || status.phase || "queued";
71
78
  const watcher = readWatcherStatus(config);
72
79
  const pendingReason = derivePendingReason({ status: status.status, phase }, watcher);
73
80
  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
 
7
8
  export function getTaskStdoutTail(taskId: string, lines = 80) {
8
9
  const config = getConfig();
@@ -19,7 +20,10 @@ export function getTaskStdoutTail(taskId: string, lines = 80) {
19
20
  const stdoutLog = join(taskDir, "stdout.log");
20
21
  const stderrLog = join(taskDir, "stderr.log");
21
22
  const resultFile = join(taskDir, "result.md");
22
- const maxLines = Math.min(lines, 200);
23
+ const maxLines = Number.isFinite(lines)
24
+ ? Math.max(1, Math.min(200, Math.trunc(lines)))
25
+ : 80;
26
+ const stderrLines = Math.min(maxLines, 40);
23
27
 
24
28
  let stdoutTail = "";
25
29
  let stderrTail = "";
@@ -27,22 +31,20 @@ export function getTaskStdoutTail(taskId: string, lines = 80) {
27
31
 
28
32
  // All log files are optional — don't throw if missing
29
33
  if (existsSync(stdoutLog)) {
30
- const raw = readFileSync(stdoutLog, "utf-8");
31
- stdoutTail = raw.split("\n").slice(-maxLines).join("\n");
34
+ stdoutTail = readTextFileTailLinesSync(stdoutLog, maxLines, config.maxReadFileBytes);
32
35
  source = "stdout.log";
33
36
  }
34
37
  if (existsSync(stderrLog)) {
35
- const raw = readFileSync(stderrLog, "utf-8");
36
- stderrTail = raw.split("\n").slice(-Math.min(maxLines, 40)).join("\n");
38
+ stderrTail = readTextFileTailLinesSync(stderrLog, stderrLines, config.maxReadFileBytes);
37
39
  }
38
40
 
39
41
  // Fallback to result.md
40
42
  if (!stdoutTail && existsSync(resultFile)) {
41
- const raw = readFileSync(resultFile, "utf-8");
43
+ const raw = readTextFilePrefixSync(resultFile, config.maxReadFileBytes).content;
42
44
  const stdoutMatch = raw.match(/## Agent stdout\s*\n+```\s*([\s\S]*?)```/i);
43
45
  const stderrMatch = raw.match(/## Agent stderr\s*\n+```\s*([\s\S]*?)```/i);
44
46
  if (stdoutMatch) stdoutTail = stdoutMatch[1].split("\n").slice(-maxLines).join("\n");
45
- if (stderrMatch && !stderrTail) stderrTail = stderrMatch[1].split("\n").slice(-Math.min(maxLines, 40)).join("\n");
47
+ if (stderrMatch && !stderrTail) stderrTail = stderrMatch[1].split("\n").slice(-stderrLines).join("\n");
46
48
  if (source === "none") source = "result.md";
47
49
  }
48
50
 
@@ -1,19 +1,14 @@
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
-
8
- const TERMINAL_STATUSES = new Set([
9
- "done",
10
- "done_by_agent",
11
- "failed",
12
- "failed_verification",
13
- "failed_scope_violation",
14
- "failed_policy_violation",
15
- "canceled",
16
- ]);
6
+ import { redactSensitiveValue } from "../../security/contentRedaction.js";
7
+ import {
8
+ readTextFilePrefixSync,
9
+ readTextFileTailLinesSync,
10
+ } from "../../utils/boundedFile.js";
11
+ import { isTerminalTaskStatus } from "./taskStates.js";
17
12
 
18
13
  export interface TaskSummaryOutput {
19
14
  task_id: string;
@@ -134,12 +129,12 @@ export function getTaskSummary(taskId: string, options: GetTaskSummaryOptions =
134
129
  const taskDir = resolve(getTasksDir(config), taskId);
135
130
  const statusFile = join(taskDir, "status.json");
136
131
  guardReadPath(statusFile, config.workspaceRoot, config.tasksDir);
137
- const status = getTaskStatus(taskId) as any;
132
+ const status = getTaskStatus(taskId);
138
133
  const resultRead = tryReadJson(join(taskDir, "result.json"));
139
134
  const verifyRead = tryReadJson(join(taskDir, "verify.json"));
140
135
  const result = resultRead.data;
141
136
  const verify = verifyRead.data;
142
- const terminal = TERMINAL_STATUSES.has(String(status.status));
137
+ const terminal = isTerminalTaskStatus(String(status.status));
143
138
  // Phase 4: Use new_out_of_scope_changes (task-caused) for acceptance status.
144
139
  // Pre-existing external dirty files that didn't change should NOT fail acceptance.
145
140
  const outOfScope = asArray(
@@ -148,7 +143,8 @@ export function getTaskSummary(taskId: string, options: GetTaskSummaryOptions =
148
143
  ?? result.out_of_scope_changes
149
144
  ?? status.out_of_scope_changes
150
145
  );
151
- const verifyStatus = String(verify.status ?? result.verify_status ?? result.verify?.status ?? status.verify_status ?? "not_available");
146
+ const resultVerify = asRecord(result.verify);
147
+ const verifyStatus = String(verify.status ?? result.verify_status ?? resultVerify.status ?? status.verify_status ?? "not_available");
152
148
  const errors = [status.error, ...asArray(result.errors), ...asArray(result.known_issues)]
153
149
  .filter((value): value is string => typeof value === "string" && value.trim() !== "");
154
150
  const warnings = asArray(result.warnings).filter((value): value is string => typeof value === "string");
@@ -179,15 +175,15 @@ export function getTaskSummary(taskId: string, options: GetTaskSummaryOptions =
179
175
  const acceptanceFile = join(taskDir, "acceptance.json");
180
176
  if (existsSync(acceptanceFile)) {
181
177
  try {
182
- const acceptance = JSON.parse(readFileSync(acceptanceFile, "utf-8"));
178
+ const acceptance = asRecord(JSON.parse(readTextFilePrefixSync(acceptanceFile, 1024 * 1024).content));
183
179
  if (acceptance.status === "accepted") {
184
180
  acceptanceStatus = "ready_for_review";
185
- acceptanceReviewedAt = acceptance.reviewed_at || null;
186
- acceptanceReviewer = acceptance.reviewer || null;
181
+ acceptanceReviewedAt = typeof acceptance.reviewed_at === "string" ? acceptance.reviewed_at : null;
182
+ acceptanceReviewer = typeof acceptance.reviewer === "string" ? acceptance.reviewer : null;
187
183
  } else if (acceptance.status === "rejected") {
188
184
  acceptanceStatus = "failed";
189
- acceptanceReviewedAt = acceptance.reviewed_at || null;
190
- acceptanceReviewer = acceptance.reviewer || null;
185
+ acceptanceReviewedAt = typeof acceptance.reviewed_at === "string" ? acceptance.reviewed_at : null;
186
+ acceptanceReviewer = typeof acceptance.reviewer === "string" ? acceptance.reviewer : null;
191
187
  if (acceptance.notes) {
192
188
  warnings.push(`Task was rejected by ${acceptance.reviewer || "human"} at ${acceptance.reviewed_at || "unknown time"}: ${acceptance.notes}`);
193
189
  }
@@ -216,14 +212,14 @@ export function getTaskSummary(taskId: string, options: GetTaskSummaryOptions =
216
212
  const artifactHygiene = asRecord(result.artifact_hygiene ?? changedFilesRead.data.artifact_hygiene ?? {
217
213
  counts: status.artifact_hygiene_counts || {},
218
214
  });
219
- const verifyCommands = asArray(verify.commands ?? result.verify_commands ?? result.verify?.commands);
215
+ const verifyCommands = asArray(verify.commands ?? result.verify_commands ?? resultVerify.commands);
220
216
  const testLogSummary = summarizeTestLog(join(taskDir, "test.log"));
221
217
  const verificationSummary = buildVerificationSummary(verifyStatus, verifyCommands, testLogSummary);
222
218
 
223
219
  // Extract failed command detail from verify records
224
- const failedVerify = (verifyCommands as any[]).find((cmd: any) =>
225
- ["failed", "timed_out", "canceled"].includes(cmd?.status)
226
- );
220
+ const failedVerify = verifyCommands
221
+ .map(asRecord)
222
+ .find((command) => ["failed", "timed_out", "canceled"].includes(String(command.status || "")));
227
223
  const failedCommandDetail = failedVerify ? {
228
224
  command: String(failedVerify.command || ""),
229
225
  exit_code: failedVerify.exit_code ?? null,
@@ -266,7 +262,9 @@ export function getTaskSummary(taskId: string, options: GetTaskSummaryOptions =
266
262
  (status.diff_available ?? (changedFiles.length > 0)) &&
267
263
  (artifacts["diff.patch"] || artifacts["git.diff"])
268
264
  ),
269
- diff_truncated: Boolean(status.diff_truncated || result.warnings?.some?.((warning: string) => warning.includes("diff.patch was truncated"))),
265
+ diff_truncated: Boolean(status.diff_truncated || asArray(result.warnings).some(
266
+ (warning) => typeof warning === "string" && warning.includes("diff.patch was truncated"),
267
+ )),
270
268
  result_available: artifacts["result.md"],
271
269
  result_json_available: artifacts["result.json"],
272
270
  verify_available: artifacts["verify.json"],
@@ -300,7 +298,7 @@ export function getTaskSummary(taskId: string, options: GetTaskSummaryOptions =
300
298
  } as TaskSummaryOutput;
301
299
  }
302
300
 
303
- function buildCompactSummary(output: Record<string, any>, maxItems: number): CompactTaskSummaryOutput {
301
+ function buildCompactSummary(output: Record<string, unknown>, maxItems: number): CompactTaskSummaryOutput {
304
302
  const hygiene = asRecord(output.artifact_hygiene);
305
303
  const groupNames = [
306
304
  "source_changes",
@@ -317,7 +315,9 @@ function buildCompactSummary(output: Record<string, any>, maxItems: number): Com
317
315
  const manifestRead = tryReadJson(join(taskDir, "artifact_manifest.json"));
318
316
  const manifest = asRecord(manifestRead.data);
319
317
  const releaseArtifacts = asArray(manifest.artifacts);
320
- const releaseArtifactPaths = releaseArtifacts.map((a: any) => String(a.path || "")).slice(0, maxItems);
318
+ const releaseArtifactPaths = releaseArtifacts
319
+ .map((artifact) => String(asRecord(artifact).path || ""))
320
+ .slice(0, maxItems);
321
321
 
322
322
  const compact = {
323
323
  view: "compact" as const,
@@ -364,42 +364,45 @@ function normalizeMaxItems(value: number | undefined): number {
364
364
  return value;
365
365
  }
366
366
 
367
- function tryReadJson(path: string): { data: Record<string, any>; error?: string } {
367
+ function tryReadJson(path: string): { data: Record<string, unknown>; error?: string } {
368
368
  if (!existsSync(path)) return { data: {} };
369
369
  try {
370
- return { data: JSON.parse(readFileSync(path, "utf-8")) };
370
+ return { data: asRecord(JSON.parse(readTextFilePrefixSync(path, 2 * 1024 * 1024).content)) };
371
371
  } catch (error) {
372
372
  return { data: {}, error: error instanceof Error ? error.message : String(error) };
373
373
  }
374
374
  }
375
375
 
376
- function asArray(value: unknown): any[] {
376
+ function asArray(value: unknown): unknown[] {
377
377
  return Array.isArray(value) ? value : [];
378
378
  }
379
379
 
380
- function asRecord(value: unknown): Record<string, any> {
381
- return value && typeof value === "object" && !Array.isArray(value) ? value as Record<string, any> : {};
380
+ function asRecord(value: unknown): Record<string, unknown> {
381
+ return value && typeof value === "object" && !Array.isArray(value)
382
+ ? value as Record<string, unknown>
383
+ : {};
382
384
  }
383
385
 
384
386
  function summarizeTestLog(path: string): string {
385
387
  if (!existsSync(path)) return "test.log missing";
386
- const text = readFileSync(path, "utf-8");
388
+ const text = readTextFileTailLinesSync(path, 200, 512 * 1024);
387
389
  const exit = text.match(/Exit\s*code:\s*([^\r\n]+)/i)?.[1]?.trim();
388
390
  return exit ? `Exit code: ${exit}` : text.trim().slice(0, 500) || "test.log empty";
389
391
  }
390
392
 
391
393
  function readResultFallback(path: string): string {
392
394
  if (!existsSync(path)) return "";
393
- const text = readFileSync(path, "utf-8");
395
+ const text = readTextFilePrefixSync(path, 1024 * 1024).content;
394
396
  return text.match(/## Summary\s+([\s\S]*?)(?:\n## |\n---|$)/i)?.[1]?.trim().slice(0, 1000) || "";
395
397
  }
396
398
 
397
- function buildVerificationSummary(status: string, commands: any[], testLogSummary: string) {
398
- const passed = commands.filter((command) => command?.status === "passed").length;
399
- const failed = commands.filter((command) => ["failed", "timed_out", "canceled"].includes(command?.status)).length;
400
- const skipped = commands.filter((command) => command?.status === "skipped").length;
399
+ function buildVerificationSummary(status: string, commands: unknown[], testLogSummary: string) {
400
+ const commandRecords = commands.map(asRecord);
401
+ const passed = commandRecords.filter((command) => command.status === "passed").length;
402
+ const failed = commandRecords.filter((command) => ["failed", "timed_out", "canceled"].includes(String(command.status || ""))).length;
403
+ const skipped = commandRecords.filter((command) => command.status === "skipped").length;
401
404
  const evidenceText = [
402
- ...commands.flatMap((command) => [command?.stdout_tail, command?.stderr_tail]),
405
+ ...commandRecords.flatMap((command) => [command.stdout_tail, command.stderr_tail]),
403
406
  testLogSummary,
404
407
  ].filter((value): value is string => typeof value === "string").join("\n");
405
408
  const headline = extractTestHeadline(evidenceText)
@@ -430,9 +433,8 @@ function extractTestHeadline(text: string): string {
430
433
  function readLogTail(path: string, lines: number): string {
431
434
  if (!existsSync(path)) return "(file not found)";
432
435
  try {
433
- const raw = readFileSync(path, "utf-8");
434
- if (raw.length === 0) return "(empty)";
435
- return raw.split("\n").slice(-lines).join("\n");
436
+ const tail = readTextFileTailLinesSync(path, lines);
437
+ return tail || "(empty)";
436
438
  } catch {
437
439
  return "(unreadable)";
438
440
  }
@@ -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";
@@ -1,8 +1,8 @@
1
1
  import { readdirSync, readFileSync, existsSync, statSync } from "node:fs";
2
2
  import { resolve, join } from "node:path";
3
- import { getTasksDir, getPlansDir, getConfig } from "../config.js";
4
- import { guardPath } from "../security/pathGuard.js";
5
- import { readTaskRuntime } from "../taskRuntime.js";
3
+ import { getTasksDir, getPlansDir, getConfig } from "../../config.js";
4
+ import { guardPath } from "../../security/pathGuard.js";
5
+ import { readTaskRuntime } from "../../runner/taskRuntime.js";
6
6
  import type { TaskPhase, TaskStatus, AcceptanceStatus } from "./createTask.js";
7
7
  import {
8
8
  derivePendingReason,
@@ -10,7 +10,8 @@ import {
10
10
  type PendingReason,
11
11
  type WatcherState,
12
12
  type WatcherStatusSnapshot,
13
- } from "../watcherStatus.js";
13
+ } from "../../watcherStatus.js";
14
+ import { isActiveTaskStatus } from "./taskStates.js";
14
15
 
15
16
  export interface TaskEntry {
16
17
  task_id: string;
@@ -97,7 +98,7 @@ export function listTasks(input?: ListTasksInput): ListTasksOutput {
97
98
  : null;
98
99
  if (taskAcceptance !== filterAcceptance) continue;
99
100
  }
100
- if (input?.active_only && !["pending", "running", "collecting_artifacts"].includes(String(data.status))) continue;
101
+ if (input?.active_only && !isActiveTaskStatus(String(data.status))) continue;
101
102
  const normalizedRepo = String(data.repo_path || ".").replace(/\\/g, "/");
102
103
  const normalizedResolvedRepo = String(data.resolved_repo_path || "").replace(/\\/g, "/");
103
104
  if (filterRepo && normalizedRepo !== filterRepo && normalizedResolvedRepo !== filterRepo) continue;