patchwarden 1.5.0 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (597) hide show
  1. package/PatchWarden.cmd +32 -2
  2. package/README.en.md +194 -26
  3. package/README.md +160 -22
  4. package/dist/assessments/agentAssessor.d.ts +1 -1
  5. package/dist/assessments/agentAssessor.js +24 -15
  6. package/dist/assessments/assessmentStore.d.ts +1 -1
  7. package/dist/assessments/assessmentStore.js +44 -36
  8. package/dist/assessments/confirmCli.js +5 -4
  9. package/dist/config.d.ts +22 -0
  10. package/dist/config.js +98 -11
  11. package/dist/control/fileManager.d.ts +2 -0
  12. package/dist/control/fileManager.js +39 -0
  13. package/dist/control/middleware/auth.d.ts +16 -0
  14. package/dist/control/middleware/auth.js +17 -0
  15. package/dist/control/middleware/static.d.ts +3 -0
  16. package/dist/control/middleware/static.js +78 -0
  17. package/dist/control/routeTable.d.ts +24 -0
  18. package/dist/control/routeTable.js +263 -0
  19. package/dist/control/routes/audit.d.ts +15 -0
  20. package/dist/control/routes/audit.js +287 -0
  21. package/dist/control/routes/evidence.d.ts +4 -0
  22. package/dist/control/routes/evidence.js +96 -0
  23. package/dist/control/routes/lineage.d.ts +3 -0
  24. package/dist/control/routes/lineage.js +71 -0
  25. package/dist/control/routes/policy.d.ts +3 -0
  26. package/dist/control/routes/policy.js +81 -0
  27. package/dist/control/routes/process.d.ts +12 -0
  28. package/dist/control/routes/process.js +487 -0
  29. package/dist/control/routes/sessions.d.ts +30 -0
  30. package/dist/control/routes/sessions.js +287 -0
  31. package/dist/control/routes/status.d.ts +51 -0
  32. package/dist/control/routes/status.js +307 -0
  33. package/dist/control/routes/taskActions.d.ts +21 -0
  34. package/dist/control/routes/taskActions.js +197 -0
  35. package/dist/control/routes/tasks.d.ts +24 -0
  36. package/dist/control/routes/tasks.js +309 -0
  37. package/dist/control/routes/workspace.d.ts +15 -0
  38. package/dist/control/routes/workspace.js +220 -0
  39. package/dist/control/runtime.d.ts +92 -0
  40. package/dist/control/runtime.js +425 -0
  41. package/dist/control/server.d.ts +13 -0
  42. package/dist/control/server.js +150 -0
  43. package/dist/control/shared.d.ts +59 -0
  44. package/dist/control/shared.js +341 -0
  45. package/dist/controlCenter.d.ts +6 -0
  46. package/dist/controlCenter.js +7 -2197
  47. package/dist/direct/directAudit.js +234 -225
  48. package/dist/direct/directGuards.d.ts +2 -1
  49. package/dist/direct/directGuards.js +72 -37
  50. package/dist/direct/directPatch.d.ts +6 -1
  51. package/dist/direct/directPatch.js +55 -8
  52. package/dist/direct/directSessionStore.d.ts +16 -6
  53. package/dist/direct/directSessionStore.js +127 -35
  54. package/dist/direct/directVerification.js +8 -16
  55. package/dist/doctor.d.ts +18 -1
  56. package/dist/doctor.js +614 -373
  57. package/dist/goal/acceptanceEngine.d.ts +1 -1
  58. package/dist/goal/goalProgress.js +75 -69
  59. package/dist/goal/goalReport.d.ts +54 -0
  60. package/dist/goal/goalReport.js +197 -0
  61. package/dist/goal/goalStatus.d.ts +8 -0
  62. package/dist/goal/goalStatus.js +10 -3
  63. package/dist/goal/goalStore.d.ts +9 -2
  64. package/dist/goal/goalStore.js +152 -45
  65. package/dist/goal/handoffExport.js +6 -6
  66. package/dist/goal/specKitImport.d.ts +63 -0
  67. package/dist/goal/specKitImport.js +221 -0
  68. package/dist/goal/subgoalSync.js +13 -10
  69. package/dist/goal/worktreeManager.d.ts +2 -0
  70. package/dist/goal/worktreeManager.js +209 -136
  71. package/dist/httpServer.js +126 -44
  72. package/dist/index.js +7 -4
  73. package/dist/logging.d.ts +7 -1
  74. package/dist/logging.js +13 -3
  75. package/dist/policy/projectPolicy.js +5 -2
  76. package/dist/release/releaseGate.d.ts +1 -1
  77. package/dist/release/releaseGate.js +78 -25
  78. package/dist/runner/agentInvocation.d.ts +14 -0
  79. package/dist/runner/agentInvocation.js +96 -4
  80. package/dist/runner/changeCapture.d.ts +14 -6
  81. package/dist/runner/changeCapture.js +287 -81
  82. package/dist/runner/cli.js +7 -6
  83. package/dist/runner/postTaskCleanup.js +60 -7
  84. package/dist/runner/processSecurity.d.ts +56 -0
  85. package/dist/runner/processSecurity.js +373 -0
  86. package/dist/runner/runTask.d.ts +1 -1
  87. package/dist/runner/runTask.js +339 -287
  88. package/dist/runner/simpleProcess.d.ts +3 -0
  89. package/dist/runner/simpleProcess.js +52 -38
  90. package/dist/{taskProgress.d.ts → runner/taskProgress.d.ts} +1 -1
  91. package/dist/{taskProgress.js → runner/taskProgress.js} +2 -2
  92. package/dist/{taskRuntime.d.ts → runner/taskRuntime.d.ts} +1 -1
  93. package/dist/{taskRuntime.js → runner/taskRuntime.js} +3 -2
  94. package/dist/runner/taskStatusStore.d.ts +11 -0
  95. package/dist/runner/taskStatusStore.js +24 -0
  96. package/dist/runner/watch.d.ts +14 -1
  97. package/dist/runner/watch.js +265 -54
  98. package/dist/security/contentRedaction.d.ts +6 -0
  99. package/dist/security/contentRedaction.js +24 -2
  100. package/dist/security/discoveryTokenStore.d.ts +3 -2
  101. package/dist/security/discoveryTokenStore.js +17 -2
  102. package/dist/security/loopbackHost.d.ts +2 -0
  103. package/dist/security/loopbackHost.js +16 -0
  104. package/dist/security/pathGuard.js +32 -17
  105. package/dist/security/planGuard.js +2 -1
  106. package/dist/security/runtimeGuard.js +9 -6
  107. package/dist/security/secretComparison.d.ts +3 -0
  108. package/dist/security/secretComparison.js +11 -0
  109. package/dist/security/sensitiveGuard.d.ts +5 -0
  110. package/dist/security/sensitiveGuard.js +27 -8
  111. package/dist/security/toolInvocationGuard.d.ts +2 -2
  112. package/dist/security/toolInvocationGuard.js +1 -1
  113. package/dist/security/workspaceRootGuard.d.ts +8 -0
  114. package/dist/security/workspaceRootGuard.js +47 -0
  115. package/dist/smoke-test.js +280 -273
  116. package/dist/tools/catalog/index.d.ts +4 -0
  117. package/dist/tools/catalog/index.js +4 -0
  118. package/dist/tools/{toolCatalog.js → catalog/toolCatalog.js} +3 -12
  119. package/dist/tools/{toolRegistry.d.ts → catalog/toolRegistry.d.ts} +3 -3
  120. package/dist/tools/{toolRegistry.js → catalog/toolRegistry.js} +28 -15
  121. package/dist/tools/definitions/toolDefs.d.ts +18 -0
  122. package/dist/tools/definitions/toolDefs.js +1177 -0
  123. package/dist/tools/{auditSession.d.ts → diagnostics/auditSession.d.ts} +1 -1
  124. package/dist/tools/{auditSession.js → diagnostics/auditSession.js} +2 -2
  125. package/dist/tools/{auditTask.d.ts → diagnostics/auditTask.d.ts} +8 -63
  126. package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +111 -37
  127. package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +6 -6
  128. package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +10 -10
  129. package/dist/tools/diagnostics/index.d.ts +6 -0
  130. package/dist/tools/diagnostics/index.js +6 -0
  131. package/dist/tools/{safeStatus.d.ts → diagnostics/safeStatus.d.ts} +3 -3
  132. package/dist/tools/{safeStatus.js → diagnostics/safeStatus.js} +6 -6
  133. package/dist/tools/{safeViews.d.ts → diagnostics/safeViews.d.ts} +14 -10
  134. package/dist/tools/{safeViews.js → diagnostics/safeViews.js} +25 -15
  135. package/dist/tools/{schemaDriftCheck.d.ts → diagnostics/schemaDriftCheck.d.ts} +1 -1
  136. package/dist/tools/{schemaDriftCheck.js → diagnostics/schemaDriftCheck.js} +2 -2
  137. package/dist/tools/{createDirectSession.d.ts → direct/createDirectSession.d.ts} +1 -1
  138. package/dist/tools/{createDirectSession.js → direct/createDirectSession.js} +8 -8
  139. package/dist/tools/{finalizeDirectSession.d.ts → direct/finalizeDirectSession.d.ts} +2 -2
  140. package/dist/tools/direct/finalizeDirectSession.js +86 -0
  141. package/dist/tools/direct/index.d.ts +3 -0
  142. package/dist/tools/direct/index.js +3 -0
  143. package/dist/tools/{runDirectVerificationBundle.js → direct/runDirectVerificationBundle.js} +2 -2
  144. package/dist/tools/{discoverTools.d.ts → discovery/discoverTools.d.ts} +2 -2
  145. package/dist/tools/{discoverTools.js → discovery/discoverTools.js} +5 -5
  146. package/dist/tools/{explainTool.d.ts → discovery/explainTool.d.ts} +2 -2
  147. package/dist/tools/{explainTool.js → discovery/explainTool.js} +2 -2
  148. package/dist/tools/discovery/index.d.ts +3 -0
  149. package/dist/tools/discovery/index.js +3 -0
  150. package/dist/tools/{invokeDiscoveredTool.d.ts → discovery/invokeDiscoveredTool.d.ts} +2 -2
  151. package/dist/tools/{invokeDiscoveredTool.js → discovery/invokeDiscoveredTool.js} +5 -5
  152. package/dist/tools/dispatch/coreDispatch.d.ts +9 -0
  153. package/dist/tools/dispatch/coreDispatch.js +283 -0
  154. package/dist/tools/dispatch/diagnosticDispatch.d.ts +12 -0
  155. package/dist/tools/dispatch/diagnosticDispatch.js +80 -0
  156. package/dist/tools/dispatch/directDispatch.d.ts +8 -0
  157. package/dist/tools/dispatch/directDispatch.js +116 -0
  158. package/dist/tools/dispatch/goalDispatch.d.ts +8 -0
  159. package/dist/tools/dispatch/goalDispatch.js +92 -0
  160. package/dist/tools/dispatch/releaseDispatch.d.ts +8 -0
  161. package/dist/tools/dispatch/releaseDispatch.js +46 -0
  162. package/dist/tools/dispatch/types.d.ts +23 -0
  163. package/dist/tools/dispatch/types.js +15 -0
  164. package/dist/tools/dispatch/validation.d.ts +7 -0
  165. package/dist/tools/dispatch/validation.js +71 -0
  166. package/dist/tools/{getPlan.js → goals/getPlan.js} +4 -4
  167. package/dist/tools/{goalSubgoalTask.d.ts → goals/goalSubgoalTask.d.ts} +2 -2
  168. package/dist/tools/goals/goalSubgoalTask.js +118 -0
  169. package/dist/tools/goals/index.d.ts +3 -0
  170. package/dist/tools/goals/index.js +3 -0
  171. package/dist/tools/{savePlan.js → goals/savePlan.js} +28 -8
  172. package/dist/tools/registry.d.ts +10 -14
  173. package/dist/tools/registry.js +47 -1651
  174. package/dist/tools/{checkReleaseGate.d.ts → release/checkReleaseGate.d.ts} +1 -1
  175. package/dist/tools/{checkReleaseGate.js → release/checkReleaseGate.js} +3 -3
  176. package/dist/tools/release/index.d.ts +2 -0
  177. package/dist/tools/release/index.js +2 -0
  178. package/dist/tools/{releaseMode.d.ts → release/releaseMode.d.ts} +2 -2
  179. package/dist/tools/{releaseMode.js → release/releaseMode.js} +46 -26
  180. package/dist/tools/tasks/cancelTask.d.ts +11 -0
  181. package/dist/tools/tasks/cancelTask.js +85 -0
  182. package/dist/tools/{createTask.d.ts → tasks/createTask.d.ts} +12 -4
  183. package/dist/tools/{createTask.js → tasks/createTask.js} +46 -31
  184. package/dist/tools/{diagnoseTask.d.ts → tasks/diagnoseTask.d.ts} +1 -1
  185. package/dist/tools/{diagnoseTask.js → tasks/diagnoseTask.js} +8 -8
  186. package/dist/tools/{evidencePack.d.ts → tasks/evidencePack.d.ts} +6 -0
  187. package/dist/tools/tasks/evidencePack.js +387 -0
  188. package/dist/tools/{getTaskFile.js → tasks/getTaskFile.js} +4 -4
  189. package/dist/tools/{getTaskStatus.d.ts → tasks/getTaskStatus.d.ts} +6 -1
  190. package/dist/tools/{getTaskStatus.js → tasks/getTaskStatus.js} +5 -5
  191. package/dist/tools/{getTaskStdoutTail.js → tasks/getTaskStdoutTail.js} +13 -11
  192. package/dist/tools/{getTaskSummary.js → tasks/getTaskSummary.js} +33 -26
  193. package/dist/tools/tasks/index.d.ts +18 -0
  194. package/dist/tools/tasks/index.js +18 -0
  195. package/dist/tools/tasks/killTask.d.ts +1 -0
  196. package/dist/tools/{listTasks.d.ts → tasks/listTasks.d.ts} +1 -1
  197. package/dist/tools/{listTasks.js → tasks/listTasks.js} +3 -3
  198. package/dist/tools/{reconcileTasks.d.ts → tasks/reconcileTasks.d.ts} +1 -1
  199. package/dist/tools/{reconcileTasks.js → tasks/reconcileTasks.js} +64 -66
  200. package/dist/tools/{retryTask.d.ts → tasks/retryTask.d.ts} +2 -2
  201. package/dist/tools/tasks/retryTask.js +60 -0
  202. package/dist/tools/{runTaskLoop.d.ts → tasks/runTaskLoop.d.ts} +6 -6
  203. package/dist/tools/{runTaskLoop.js → tasks/runTaskLoop.js} +59 -31
  204. package/dist/tools/tasks/runVerification.js +38 -0
  205. package/dist/tools/{taskLineage.js → tasks/taskLineage.js} +8 -7
  206. package/dist/tools/{taskOutputs.js → tasks/taskOutputs.js} +17 -16
  207. package/dist/tools/{androidDoctor.js → workspace/androidDoctor.js} +16 -10
  208. package/dist/tools/{applyPatch.d.ts → workspace/applyPatch.d.ts} +1 -1
  209. package/dist/tools/workspace/applyPatch.js +53 -0
  210. package/dist/tools/{discardWorktree.js → workspace/discardWorktree.js} +3 -3
  211. package/dist/tools/workspace/index.d.ts +10 -0
  212. package/dist/tools/workspace/index.js +10 -0
  213. package/dist/tools/{listAgents.d.ts → workspace/listAgents.d.ts} +5 -0
  214. package/dist/tools/{listAgents.js → workspace/listAgents.js} +8 -4
  215. package/dist/tools/{listWorkspace.js → workspace/listWorkspace.js} +3 -3
  216. package/dist/tools/{mergeWorktree.js → workspace/mergeWorktree.js} +3 -3
  217. package/dist/tools/{readWorkspaceFile.js → workspace/readWorkspaceFile.js} +8 -8
  218. package/dist/tools/{recommendAgentForTask.js → workspace/recommendAgentForTask.js} +3 -3
  219. package/dist/tools/{searchWorkspace.js → workspace/searchWorkspace.js} +5 -5
  220. package/dist/tools/{syncFile.d.ts → workspace/syncFile.d.ts} +1 -1
  221. package/dist/tools/workspace/syncFile.js +134 -0
  222. package/dist/utils/atomicFile.d.ts +7 -0
  223. package/dist/utils/atomicFile.js +48 -0
  224. package/dist/utils/boundedFile.d.ts +8 -0
  225. package/dist/utils/boundedFile.js +93 -0
  226. package/dist/utils/lockedJsonFile.d.ts +15 -0
  227. package/dist/utils/lockedJsonFile.js +224 -0
  228. package/dist/utils/platform.d.ts +11 -0
  229. package/dist/utils/platform.js +26 -0
  230. package/dist/utils/stableJson.d.ts +2 -0
  231. package/dist/utils/stableJson.js +12 -0
  232. package/dist/version.d.ts +3 -2
  233. package/dist/version.js +3 -2
  234. package/dist/watcherStatus.d.ts +2 -2
  235. package/dist/watcherStatus.js +18 -0
  236. package/docs/CODE_WIKI.md +1648 -0
  237. package/docs/agentseal-integration.md +150 -0
  238. package/docs/architecture.md +23 -0
  239. package/docs/assets/patchwarden-desktop-onboarding.png +0 -0
  240. package/docs/assets/patchwarden-oss-demo.gif +0 -0
  241. package/docs/control-center/README.md +4 -4
  242. package/docs/dashboard-overview.md +86 -0
  243. package/docs/demo.md +8 -0
  244. package/docs/desktop-app.md +175 -0
  245. package/docs/direct-session-workflow.md +98 -0
  246. package/docs/evidence-pack-schema.md +215 -0
  247. package/docs/lineage-evidence-pack-workflow.md +127 -0
  248. package/docs/mcp-inspector-testing.md +200 -0
  249. package/docs/open-source-application.md +168 -0
  250. package/docs/opencode-worker.md +151 -0
  251. package/docs/openhands-worker.md +181 -0
  252. package/docs/release-checklist.md +1 -1
  253. package/docs/release-evidence.md +82 -0
  254. package/docs/spec-kit-integration.md +131 -0
  255. package/docs/task-safe-review-workflow.md +98 -0
  256. package/docs/threat-model.md +97 -0
  257. package/docs/user-feedback.md +40 -0
  258. package/docs/why-patchwarden.md +110 -0
  259. package/examples/config.example.json +4 -2
  260. package/package.json +28 -11
  261. package/scripts/README.md +15 -6
  262. package/scripts/build.js +11 -0
  263. package/scripts/checks/brand-check.js +3 -0
  264. package/scripts/checks/build-output-check.js +37 -0
  265. package/scripts/checks/control-center-smoke.js +447 -6
  266. package/scripts/checks/control-smoke.js +19 -8
  267. package/scripts/checks/http-mcp-smoke.js +82 -4
  268. package/scripts/checks/lifecycle-smoke.js +39 -33
  269. package/scripts/checks/mcp-manifest-check.js +1 -1
  270. package/scripts/checks/mcp-smoke.js +30 -4
  271. package/scripts/checks/package-manifest-check.js +20 -7
  272. package/scripts/checks/unit-tests.js +3 -5
  273. package/scripts/checks/watcher-supervisor-smoke.js +9 -0
  274. package/scripts/control/get-patchwarden-health.ps1 +1 -1
  275. package/scripts/control/manage-patchwarden.ps1 +69 -4
  276. package/scripts/control/provision-patchwarden-tunnel.ps1 +127 -0
  277. package/scripts/control/run-background-supervisor.ps1 +41 -0
  278. package/scripts/control/start-patchwarden-tunnel.ps1 +126 -32
  279. package/scripts/generate-demo-gif.py +320 -0
  280. package/{PatchWarden-Control-Tray.cmd → scripts/launchers/PatchWarden-Control-Tray.cmd} +3 -3
  281. package/scripts/launchers/PatchWarden-Control.cmd +6 -0
  282. package/{PatchWarden-Desktop.cmd → scripts/launchers/PatchWarden-Desktop.cmd} +2 -2
  283. package/scripts/launchers/Restart-PatchWarden-Control.cmd +6 -0
  284. package/{Stop-PatchWarden.cmd → scripts/launchers/Stop-PatchWarden.cmd} +2 -2
  285. package/scripts/lib/clean-generated-output.js +11 -0
  286. package/scripts/lib/file-discovery.js +12 -0
  287. package/scripts/release/desktop-preflight.js +211 -0
  288. package/scripts/release/pack-clean.js +59 -14
  289. package/src/assessments/agentAssessor.ts +29 -15
  290. package/src/assessments/assessmentStore.ts +55 -43
  291. package/src/assessments/confirmCli.ts +5 -4
  292. package/src/config.ts +105 -11
  293. package/src/control/fileManager.ts +40 -0
  294. package/src/control/middleware/auth.ts +35 -0
  295. package/src/control/middleware/static.ts +84 -0
  296. package/src/control/routeTable.ts +301 -0
  297. package/src/control/routes/audit.ts +329 -0
  298. package/src/control/routes/evidence.ts +107 -0
  299. package/src/control/routes/lineage.ts +92 -0
  300. package/src/control/routes/policy.ts +81 -0
  301. package/src/control/routes/process.ts +505 -0
  302. package/src/control/routes/sessions.ts +314 -0
  303. package/src/control/routes/status.ts +405 -0
  304. package/src/control/routes/taskActions.ts +214 -0
  305. package/src/control/routes/tasks.ts +331 -0
  306. package/src/control/routes/workspace.ts +234 -0
  307. package/src/control/runtime.ts +508 -0
  308. package/src/control/server.ts +165 -0
  309. package/src/control/shared.ts +360 -0
  310. package/src/controlCenter.ts +7 -2347
  311. package/src/direct/directAudit.ts +17 -9
  312. package/src/direct/directGuards.ts +100 -51
  313. package/src/direct/directPatch.ts +95 -8
  314. package/src/direct/directSessionStore.ts +211 -55
  315. package/src/direct/directVerification.ts +9 -23
  316. package/src/doctor.ts +766 -492
  317. package/src/goal/acceptanceEngine.ts +1 -1
  318. package/src/goal/goalProgress.ts +17 -13
  319. package/src/goal/goalReport.ts +263 -0
  320. package/src/goal/goalStatus.ts +25 -3
  321. package/src/goal/goalStore.ts +241 -54
  322. package/src/goal/handoffExport.ts +6 -6
  323. package/src/goal/specKitImport.ts +355 -0
  324. package/src/goal/subgoalSync.ts +15 -10
  325. package/src/goal/worktreeManager.ts +139 -22
  326. package/src/httpServer.ts +130 -44
  327. package/src/index.ts +7 -4
  328. package/src/logging.ts +20 -4
  329. package/src/policy/projectPolicy.ts +5 -2
  330. package/src/release/releaseGate.ts +74 -27
  331. package/src/runner/agentInvocation.ts +126 -4
  332. package/src/runner/changeCapture.ts +342 -80
  333. package/src/runner/cli.ts +7 -6
  334. package/src/runner/postTaskCleanup.ts +55 -7
  335. package/src/runner/processSecurity.ts +433 -0
  336. package/src/runner/runTask.ts +441 -292
  337. package/src/runner/simpleProcess.ts +68 -33
  338. package/src/{taskProgress.ts → runner/taskProgress.ts} +3 -3
  339. package/src/{taskRuntime.ts → runner/taskRuntime.ts} +4 -3
  340. package/src/runner/taskStatusStore.ts +54 -0
  341. package/src/runner/watch.ts +290 -57
  342. package/src/security/contentRedaction.ts +31 -2
  343. package/src/security/discoveryTokenStore.ts +18 -4
  344. package/src/security/loopbackHost.ts +19 -0
  345. package/src/security/pathGuard.ts +35 -16
  346. package/src/security/planGuard.ts +2 -1
  347. package/src/security/runtimeGuard.ts +9 -6
  348. package/src/security/secretComparison.ts +13 -0
  349. package/src/security/sensitiveGuard.ts +27 -8
  350. package/src/security/toolInvocationGuard.ts +2 -2
  351. package/src/security/workspaceRootGuard.ts +53 -0
  352. package/src/smoke-test.ts +276 -273
  353. package/src/tools/catalog/index.ts +4 -0
  354. package/src/tools/{toolCatalog.ts → catalog/toolCatalog.ts} +3 -12
  355. package/src/tools/{toolRegistry.ts → catalog/toolRegistry.ts} +29 -16
  356. package/src/tools/definitions/toolDefs.ts +1264 -0
  357. package/src/tools/{auditSession.ts → diagnostics/auditSession.ts} +2 -2
  358. package/src/tools/{auditTask.ts → diagnostics/auditTask.ts} +196 -89
  359. package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +11 -11
  360. package/src/tools/diagnostics/index.ts +6 -0
  361. package/src/tools/{safeStatus.ts → diagnostics/safeStatus.ts} +7 -7
  362. package/src/tools/{safeViews.ts → diagnostics/safeViews.ts} +29 -19
  363. package/src/tools/{schemaDriftCheck.ts → diagnostics/schemaDriftCheck.ts} +3 -3
  364. package/src/tools/{createDirectSession.ts → direct/createDirectSession.ts} +9 -9
  365. package/src/tools/{finalizeDirectSession.ts → direct/finalizeDirectSession.ts} +11 -9
  366. package/src/tools/direct/index.ts +3 -0
  367. package/src/tools/{runDirectVerificationBundle.ts → direct/runDirectVerificationBundle.ts} +2 -2
  368. package/src/tools/{discoverTools.ts → discovery/discoverTools.ts} +6 -6
  369. package/src/tools/{explainTool.ts → discovery/explainTool.ts} +3 -3
  370. package/src/tools/discovery/index.ts +3 -0
  371. package/src/tools/{invokeDiscoveredTool.ts → discovery/invokeDiscoveredTool.ts} +7 -7
  372. package/src/tools/dispatch/coreDispatch.ts +379 -0
  373. package/src/tools/dispatch/diagnosticDispatch.ts +106 -0
  374. package/src/tools/dispatch/directDispatch.ts +168 -0
  375. package/src/tools/dispatch/goalDispatch.ts +128 -0
  376. package/src/tools/dispatch/releaseDispatch.ts +66 -0
  377. package/src/tools/dispatch/types.ts +24 -0
  378. package/src/tools/dispatch/validation.ts +78 -0
  379. package/src/tools/{getPlan.ts → goals/getPlan.ts} +4 -4
  380. package/src/tools/{goalSubgoalTask.ts → goals/goalSubgoalTask.ts} +43 -28
  381. package/src/tools/goals/index.ts +3 -0
  382. package/src/tools/{savePlan.ts → goals/savePlan.ts} +38 -8
  383. package/src/tools/registry.ts +54 -1858
  384. package/src/tools/{checkReleaseGate.ts → release/checkReleaseGate.ts} +3 -3
  385. package/src/tools/release/index.ts +2 -0
  386. package/src/tools/{releaseMode.ts → release/releaseMode.ts} +42 -24
  387. package/src/tools/tasks/cancelTask.ts +109 -0
  388. package/src/tools/{createTask.ts → tasks/createTask.ts} +56 -33
  389. package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +8 -8
  390. package/src/tools/tasks/evidencePack.ts +504 -0
  391. package/src/tools/{getTaskFile.ts → tasks/getTaskFile.ts} +4 -4
  392. package/src/tools/{getTaskStatus.ts → tasks/getTaskStatus.ts} +7 -5
  393. package/src/tools/{getTaskStdoutTail.ts → tasks/getTaskStdoutTail.ts} +13 -11
  394. package/src/tools/{getTaskSummary.ts → tasks/getTaskSummary.ts} +44 -33
  395. package/src/tools/tasks/index.ts +18 -0
  396. package/src/tools/{listTasks.ts → tasks/listTasks.ts} +4 -4
  397. package/src/tools/{reconcileTasks.ts → tasks/reconcileTasks.ts} +68 -66
  398. package/src/tools/tasks/retryTask.ts +69 -0
  399. package/src/tools/{runTaskLoop.ts → tasks/runTaskLoop.ts} +70 -35
  400. package/src/tools/{runVerification.ts → tasks/runVerification.ts} +6 -3
  401. package/src/tools/{taskLineage.ts → tasks/taskLineage.ts} +8 -7
  402. package/src/tools/{taskOutputs.ts → tasks/taskOutputs.ts} +27 -20
  403. package/src/tools/{androidDoctor.ts → workspace/androidDoctor.ts} +15 -10
  404. package/src/tools/{applyPatch.ts → workspace/applyPatch.ts} +38 -16
  405. package/src/tools/{discardWorktree.ts → workspace/discardWorktree.ts} +3 -3
  406. package/src/tools/workspace/index.ts +10 -0
  407. package/src/tools/{listAgents.ts → workspace/listAgents.ts} +11 -4
  408. package/src/tools/{listWorkspace.ts → workspace/listWorkspace.ts} +3 -3
  409. package/src/tools/{mergeWorktree.ts → workspace/mergeWorktree.ts} +3 -3
  410. package/src/tools/{readWorkspaceFile.ts → workspace/readWorkspaceFile.ts} +8 -8
  411. package/src/tools/{recommendAgentForTask.ts → workspace/recommendAgentForTask.ts} +3 -3
  412. package/src/tools/{searchWorkspace.ts → workspace/searchWorkspace.ts} +5 -5
  413. package/src/tools/workspace/syncFile.ts +292 -0
  414. package/src/utils/atomicFile.ts +58 -0
  415. package/src/utils/boundedFile.ts +116 -0
  416. package/src/utils/lockedJsonFile.ts +251 -0
  417. package/src/utils/platform.ts +31 -0
  418. package/src/utils/stableJson.ts +12 -0
  419. package/src/version.ts +3 -2
  420. package/src/watcherStatus.ts +19 -1
  421. package/ui/desktop-bootstrap.js +11 -0
  422. package/ui/desktop-bridge.js +36 -0
  423. package/ui/desktop.css +160 -0
  424. package/ui/getting-started.js +93 -0
  425. package/ui/i18n.js +484 -0
  426. package/ui/log-parser.js +30 -0
  427. package/ui/pages/audit.html +204 -3
  428. package/ui/pages/dashboard.html +937 -79
  429. package/ui/pages/direct-sessions.html +609 -60
  430. package/ui/pages/getting-started.html +43 -0
  431. package/ui/pages/logs.html +105 -22
  432. package/ui/pages/settings.html +101 -0
  433. package/ui/pages/task-detail.html +468 -438
  434. package/ui/pages/tasks.html +648 -65
  435. package/ui/pages/workspace.html +38 -1
  436. package/ui/settings.js +245 -0
  437. package/PatchWarden-Control.cmd +0 -6
  438. package/Restart-PatchWarden-Control.cmd +0 -6
  439. package/dist/test/unit/acceptance-engine.test.d.ts +0 -1
  440. package/dist/test/unit/acceptance-engine.test.js +0 -228
  441. package/dist/test/unit/agent-router.test.d.ts +0 -1
  442. package/dist/test/unit/agent-router.test.js +0 -287
  443. package/dist/test/unit/android-doctor.test.d.ts +0 -1
  444. package/dist/test/unit/android-doctor.test.js +0 -118
  445. package/dist/test/unit/audit-checks.test.d.ts +0 -1
  446. package/dist/test/unit/audit-checks.test.js +0 -350
  447. package/dist/test/unit/chinese-path.test.d.ts +0 -1
  448. package/dist/test/unit/chinese-path.test.js +0 -91
  449. package/dist/test/unit/command-guard.test.d.ts +0 -1
  450. package/dist/test/unit/command-guard.test.js +0 -160
  451. package/dist/test/unit/diagnose-task.test.d.ts +0 -1
  452. package/dist/test/unit/diagnose-task.test.js +0 -457
  453. package/dist/test/unit/direct-guards.test.d.ts +0 -1
  454. package/dist/test/unit/direct-guards.test.js +0 -213
  455. package/dist/test/unit/discovery-token-store.test.d.ts +0 -1
  456. package/dist/test/unit/discovery-token-store.test.js +0 -139
  457. package/dist/test/unit/evidence-pack.test.d.ts +0 -1
  458. package/dist/test/unit/evidence-pack.test.js +0 -130
  459. package/dist/test/unit/goal-graph.test.d.ts +0 -1
  460. package/dist/test/unit/goal-graph.test.js +0 -298
  461. package/dist/test/unit/goal-progress.test.d.ts +0 -1
  462. package/dist/test/unit/goal-progress.test.js +0 -381
  463. package/dist/test/unit/goal-status.test.d.ts +0 -1
  464. package/dist/test/unit/goal-status.test.js +0 -215
  465. package/dist/test/unit/goal-store.test.d.ts +0 -1
  466. package/dist/test/unit/goal-store.test.js +0 -253
  467. package/dist/test/unit/goal-subgoal-task.test.d.ts +0 -1
  468. package/dist/test/unit/goal-subgoal-task.test.js +0 -55
  469. package/dist/test/unit/goal-tools-registry.test.d.ts +0 -1
  470. package/dist/test/unit/goal-tools-registry.test.js +0 -190
  471. package/dist/test/unit/handoff-export.test.d.ts +0 -1
  472. package/dist/test/unit/handoff-export.test.js +0 -263
  473. package/dist/test/unit/invoke-discovered-tool.test.d.ts +0 -1
  474. package/dist/test/unit/invoke-discovered-tool.test.js +0 -167
  475. package/dist/test/unit/logging.test.d.ts +0 -1
  476. package/dist/test/unit/logging.test.js +0 -397
  477. package/dist/test/unit/path-guard.test.d.ts +0 -1
  478. package/dist/test/unit/path-guard.test.js +0 -109
  479. package/dist/test/unit/post-task-cleanup.test.d.ts +0 -1
  480. package/dist/test/unit/post-task-cleanup.test.js +0 -48
  481. package/dist/test/unit/project-policy-release-mode.test.d.ts +0 -1
  482. package/dist/test/unit/project-policy-release-mode.test.js +0 -125
  483. package/dist/test/unit/reconcile-tasks.test.d.ts +0 -1
  484. package/dist/test/unit/reconcile-tasks.test.js +0 -456
  485. package/dist/test/unit/release-gate.test.d.ts +0 -1
  486. package/dist/test/unit/release-gate.test.js +0 -242
  487. package/dist/test/unit/run-task-loop.test.d.ts +0 -1
  488. package/dist/test/unit/run-task-loop.test.js +0 -380
  489. package/dist/test/unit/safe-status.test.d.ts +0 -1
  490. package/dist/test/unit/safe-status.test.js +0 -165
  491. package/dist/test/unit/safe-views.test.d.ts +0 -1
  492. package/dist/test/unit/safe-views.test.js +0 -171
  493. package/dist/test/unit/schema-drift-check.test.d.ts +0 -1
  494. package/dist/test/unit/schema-drift-check.test.js +0 -176
  495. package/dist/test/unit/sensitive-guard.test.d.ts +0 -1
  496. package/dist/test/unit/sensitive-guard.test.js +0 -104
  497. package/dist/test/unit/subgoal-sync.test.d.ts +0 -1
  498. package/dist/test/unit/subgoal-sync.test.js +0 -183
  499. package/dist/test/unit/sync-file.test.d.ts +0 -1
  500. package/dist/test/unit/sync-file.test.js +0 -154
  501. package/dist/test/unit/tool-invocation-guard.test.d.ts +0 -1
  502. package/dist/test/unit/tool-invocation-guard.test.js +0 -432
  503. package/dist/test/unit/tool-usage-stats.test.d.ts +0 -1
  504. package/dist/test/unit/tool-usage-stats.test.js +0 -300
  505. package/dist/test/unit/toolSearch.test.d.ts +0 -1
  506. package/dist/test/unit/toolSearch.test.js +0 -571
  507. package/dist/test/unit/watcher-status.test.d.ts +0 -1
  508. package/dist/test/unit/watcher-status.test.js +0 -169
  509. package/dist/test/unit/worktree-manager.test.d.ts +0 -1
  510. package/dist/test/unit/worktree-manager.test.js +0 -176
  511. package/dist/tools/applyPatch.js +0 -41
  512. package/dist/tools/cancelTask.d.ts +0 -30
  513. package/dist/tools/cancelTask.js +0 -64
  514. package/dist/tools/evidencePack.js +0 -168
  515. package/dist/tools/finalizeDirectSession.js +0 -84
  516. package/dist/tools/goalSubgoalTask.js +0 -110
  517. package/dist/tools/killTask.d.ts +0 -15
  518. package/dist/tools/retryTask.js +0 -45
  519. package/dist/tools/runVerification.js +0 -36
  520. package/dist/tools/syncFile.js +0 -65
  521. package/docs/control-center/control-center-daily-driver.md +0 -211
  522. package/docs/control-center/control-center-mvp.md +0 -205
  523. package/docs/control-center/control-center-phase2.md +0 -159
  524. package/docs/release-v0.6.0.md +0 -71
  525. package/docs/release-v0.6.1.md +0 -75
  526. package/docs/release-v0.6.4.md +0 -45
  527. package/src/test/unit/acceptance-engine.test.ts +0 -261
  528. package/src/test/unit/agent-router.test.ts +0 -342
  529. package/src/test/unit/android-doctor.test.ts +0 -158
  530. package/src/test/unit/audit-checks.test.ts +0 -567
  531. package/src/test/unit/chinese-path.test.ts +0 -106
  532. package/src/test/unit/command-guard.test.ts +0 -221
  533. package/src/test/unit/diagnose-task.test.ts +0 -544
  534. package/src/test/unit/direct-guards.test.ts +0 -297
  535. package/src/test/unit/discovery-token-store.test.ts +0 -181
  536. package/src/test/unit/evidence-pack.test.ts +0 -142
  537. package/src/test/unit/goal-graph.test.ts +0 -347
  538. package/src/test/unit/goal-progress.test.ts +0 -538
  539. package/src/test/unit/goal-status.test.ts +0 -270
  540. package/src/test/unit/goal-store.test.ts +0 -318
  541. package/src/test/unit/goal-subgoal-task.test.ts +0 -72
  542. package/src/test/unit/goal-tools-registry.test.ts +0 -243
  543. package/src/test/unit/handoff-export.test.ts +0 -295
  544. package/src/test/unit/invoke-discovered-tool.test.ts +0 -216
  545. package/src/test/unit/logging.test.ts +0 -497
  546. package/src/test/unit/path-guard.test.ts +0 -150
  547. package/src/test/unit/post-task-cleanup.test.ts +0 -53
  548. package/src/test/unit/project-policy-release-mode.test.ts +0 -156
  549. package/src/test/unit/reconcile-tasks.test.ts +0 -551
  550. package/src/test/unit/release-gate.test.ts +0 -314
  551. package/src/test/unit/run-task-loop.test.ts +0 -425
  552. package/src/test/unit/safe-status.test.ts +0 -187
  553. package/src/test/unit/safe-views.test.ts +0 -184
  554. package/src/test/unit/schema-drift-check.test.ts +0 -260
  555. package/src/test/unit/sensitive-guard.test.ts +0 -124
  556. package/src/test/unit/subgoal-sync.test.ts +0 -236
  557. package/src/test/unit/sync-file.test.ts +0 -231
  558. package/src/test/unit/tool-invocation-guard.test.ts +0 -542
  559. package/src/test/unit/tool-usage-stats.test.ts +0 -384
  560. package/src/test/unit/toolSearch.test.ts +0 -756
  561. package/src/test/unit/watcher-status.test.ts +0 -190
  562. package/src/test/unit/worktree-manager.test.ts +0 -247
  563. package/src/tools/cancelTask.ts +0 -70
  564. package/src/tools/evidencePack.ts +0 -205
  565. package/src/tools/retryTask.ts +0 -53
  566. package/src/tools/syncFile.ts +0 -122
  567. /package/dist/tools/{toolCatalog.d.ts → catalog/toolCatalog.d.ts} +0 -0
  568. /package/dist/tools/{toolSearch.d.ts → catalog/toolSearch.d.ts} +0 -0
  569. /package/dist/tools/{toolSearch.js → catalog/toolSearch.js} +0 -0
  570. /package/dist/tools/{toolUsageStats.d.ts → catalog/toolUsageStats.d.ts} +0 -0
  571. /package/dist/tools/{toolUsageStats.js → catalog/toolUsageStats.js} +0 -0
  572. /package/dist/tools/{runDirectVerificationBundle.d.ts → direct/runDirectVerificationBundle.d.ts} +0 -0
  573. /package/dist/tools/{getPlan.d.ts → goals/getPlan.d.ts} +0 -0
  574. /package/dist/tools/{savePlan.d.ts → goals/savePlan.d.ts} +0 -0
  575. /package/dist/tools/{getTaskFile.d.ts → tasks/getTaskFile.d.ts} +0 -0
  576. /package/dist/tools/{getTaskProgress.d.ts → tasks/getTaskProgress.d.ts} +0 -0
  577. /package/dist/tools/{getTaskProgress.js → tasks/getTaskProgress.js} +0 -0
  578. /package/dist/tools/{getTaskStdoutTail.d.ts → tasks/getTaskStdoutTail.d.ts} +0 -0
  579. /package/dist/tools/{getTaskSummary.d.ts → tasks/getTaskSummary.d.ts} +0 -0
  580. /package/dist/tools/{killTask.js → tasks/killTask.js} +0 -0
  581. /package/dist/tools/{runVerification.d.ts → tasks/runVerification.d.ts} +0 -0
  582. /package/dist/tools/{taskLineage.d.ts → tasks/taskLineage.d.ts} +0 -0
  583. /package/dist/tools/{taskOutputs.d.ts → tasks/taskOutputs.d.ts} +0 -0
  584. /package/dist/tools/{waitForTask.d.ts → tasks/waitForTask.d.ts} +0 -0
  585. /package/dist/tools/{waitForTask.js → tasks/waitForTask.js} +0 -0
  586. /package/dist/tools/{androidDoctor.d.ts → workspace/androidDoctor.d.ts} +0 -0
  587. /package/dist/tools/{discardWorktree.d.ts → workspace/discardWorktree.d.ts} +0 -0
  588. /package/dist/tools/{listWorkspace.d.ts → workspace/listWorkspace.d.ts} +0 -0
  589. /package/dist/tools/{mergeWorktree.d.ts → workspace/mergeWorktree.d.ts} +0 -0
  590. /package/dist/tools/{readWorkspaceFile.d.ts → workspace/readWorkspaceFile.d.ts} +0 -0
  591. /package/dist/tools/{recommendAgentForTask.d.ts → workspace/recommendAgentForTask.d.ts} +0 -0
  592. /package/dist/tools/{searchWorkspace.d.ts → workspace/searchWorkspace.d.ts} +0 -0
  593. /package/src/tools/{toolSearch.ts → catalog/toolSearch.ts} +0 -0
  594. /package/src/tools/{toolUsageStats.ts → catalog/toolUsageStats.ts} +0 -0
  595. /package/src/tools/{getTaskProgress.ts → tasks/getTaskProgress.ts} +0 -0
  596. /package/src/tools/{killTask.ts → tasks/killTask.ts} +0 -0
  597. /package/src/tools/{waitForTask.ts → tasks/waitForTask.ts} +0 -0
@@ -0,0 +1,287 @@
1
+ /**
2
+ * Control Center routes — Direct sessions (/api/direct-sessions/*).
3
+ *
4
+ * Lists, inspects, finalizes, audits, and hides Direct editing sessions. The
5
+ * list/detail endpoints are read-only and fault-tolerant (missing dir returns
6
+ * an empty list, never 500). The finalize/audit/hide endpoints are POST routes
7
+ * gated by the control token in the server router.
8
+ */
9
+ import { existsSync, readdirSync, statSync } from "node:fs";
10
+ import { join } from "node:path";
11
+ import { getDirectSessionsDir } from "../../config.js";
12
+ import { safeAuditDirectSession, safeDirectSummary, safeFinalizeDirectSession } from "../../tools/diagnostics/safeViews.js";
13
+ import { fileMtimeIso, isValidDirectSessionId, readHiddenDirectSessionIds, recordEvent, writeHiddenDirectSessionIds, } from "../runtime.js";
14
+ import { config, errorMessage, guardControlPath, readJsonFileSafe, readTextFileSafe, sendJson } from "../shared.js";
15
+ function readDirectSessionSummary(sessionDir, sessionId) {
16
+ const sessionFile = join(sessionDir, "session.json");
17
+ if (!existsSync(sessionFile))
18
+ return null;
19
+ const data = readJsonFileSafe(sessionFile);
20
+ if (!data)
21
+ return null;
22
+ // summary.json holds the finalized change summary (changed_files_total, etc.)
23
+ const summaryFile = join(sessionDir, "summary.json");
24
+ const summary = readJsonFileSafe(summaryFile);
25
+ const changedFilesTotal = summary
26
+ ? typeof summary.changed_files_total === "number"
27
+ ? summary.changed_files_total
28
+ : null
29
+ : null;
30
+ // audit.json (written by audit_session) holds the audit decision
31
+ const auditFile = join(sessionDir, "audit.json");
32
+ const audit = readJsonFileSafe(auditFile);
33
+ const auditDecision = audit
34
+ ? typeof audit.decision === "string"
35
+ ? audit.decision
36
+ : typeof audit.verdict === "string"
37
+ ? audit.verdict
38
+ : null
39
+ : null;
40
+ const auditCheckedAt = audit
41
+ ? typeof audit.checked_at === "string"
42
+ ? audit.checked_at
43
+ : fileMtimeIso(auditFile)
44
+ : null;
45
+ // verification summary: read from session.json verification_runs (last run)
46
+ let verificationSummary = null;
47
+ if (Array.isArray(data.verification_runs) && data.verification_runs.length > 0) {
48
+ const runs = data.verification_runs;
49
+ verificationSummary = runs[runs.length - 1];
50
+ }
51
+ return {
52
+ session_id: sessionId,
53
+ repo_path: typeof data.repo_path === "string" ? data.repo_path : "",
54
+ resolved_repo_path: typeof data.resolved_repo_path === "string" ? data.resolved_repo_path : "",
55
+ created_at: typeof data.created_at === "string" ? data.created_at : "",
56
+ expires_at: typeof data.expires_at === "string" ? data.expires_at : "",
57
+ finalized: Boolean(data.finalized),
58
+ finalized_at: typeof data.finalized_at === "string" ? data.finalized_at : null,
59
+ audited: Boolean(data.audited),
60
+ changed_files_total: changedFilesTotal,
61
+ verification_summary: verificationSummary,
62
+ audit_decision: auditDecision,
63
+ audit_checked_at: auditCheckedAt,
64
+ title: typeof data.title === "string" ? data.title : "",
65
+ };
66
+ }
67
+ export function handleDirectSessions(res, filters) {
68
+ try {
69
+ const sessionsDir = getDirectSessionsDir(config);
70
+ if (!existsSync(sessionsDir)) {
71
+ // Directory missing -> empty list, never 500.
72
+ sendJson(res, 200, emptyDirectSessionResponse(filters));
73
+ return;
74
+ }
75
+ let entries = [];
76
+ try {
77
+ entries = readdirSync(sessionsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
78
+ }
79
+ catch (err) {
80
+ sendJson(res, 200, { ...emptyDirectSessionResponse(filters), reason: errorMessage(err) });
81
+ return;
82
+ }
83
+ // Filter out sessions hidden from the dashboard via POST .../hide.
84
+ const hiddenIds = new Set(readHiddenDirectSessionIds());
85
+ const summaries = [];
86
+ for (const entry of entries) {
87
+ if (hiddenIds.has(entry.name))
88
+ continue;
89
+ const sessionDir = guardControlPath(join(sessionsDir, entry.name), config.directSessionsDir);
90
+ if (!sessionDir)
91
+ continue;
92
+ const summary = readDirectSessionSummary(sessionDir, entry.name);
93
+ if (summary)
94
+ summaries.push(summary);
95
+ }
96
+ // Sort by created_at descending, then apply archive filters and pagination.
97
+ summaries.sort((a, b) => b.created_at.localeCompare(a.created_at));
98
+ const now = Date.now();
99
+ let filtered = summaries;
100
+ if (filters?.state) {
101
+ filtered = filtered.filter((session) => {
102
+ const expired = Boolean(session.expires_at) && Date.parse(session.expires_at) < now;
103
+ if (filters.state === "archive")
104
+ return session.finalized || session.audited || expired;
105
+ if (filters.state === "expired")
106
+ return expired;
107
+ if (filters.state === "audited")
108
+ return session.audited && !expired;
109
+ if (filters.state === "finalized")
110
+ return session.finalized && !session.audited && !expired;
111
+ return !session.finalized && !expired;
112
+ });
113
+ }
114
+ if (filters?.repo_path)
115
+ filtered = filtered.filter((session) => session.repo_path === filters.repo_path);
116
+ if (filters?.date_from || filters?.date_to) {
117
+ const from = filters.date_from ? Date.parse(`${filters.date_from}T00:00:00`) : Number.NEGATIVE_INFINITY;
118
+ const to = filters.date_to ? Date.parse(`${filters.date_to}T23:59:59`) : Number.POSITIVE_INFINITY;
119
+ filtered = filtered.filter((session) => {
120
+ const timestamp = Date.parse(session.created_at);
121
+ return Number.isFinite(timestamp) && timestamp >= from && timestamp <= to;
122
+ });
123
+ }
124
+ const limit = Math.max(10, Math.min(filters?.limit || 30, 100));
125
+ const offset = filters?.cursor && /^\d+$/.test(filters.cursor) ? Number(filters.cursor) : 0;
126
+ const sessions = filtered.slice(offset, offset + limit);
127
+ const nextCursor = offset + sessions.length < filtered.length ? String(offset + sessions.length) : null;
128
+ const repos = [...new Set(summaries.map((session) => session.repo_path).filter(Boolean))].sort();
129
+ sendJson(res, 200, {
130
+ sessions,
131
+ total: filtered.length,
132
+ returned: sessions.length,
133
+ nextCursor,
134
+ next_cursor: nextCursor,
135
+ filters: {
136
+ applied: {
137
+ state: filters?.state || null,
138
+ repo_path: filters?.repo_path || null,
139
+ date_from: filters?.date_from || null,
140
+ date_to: filters?.date_to || null,
141
+ },
142
+ options: { repos, states: ["active", "archive", "finalized", "audited", "expired"] },
143
+ },
144
+ direct_profile_enabled: config.enableDirectProfile === true,
145
+ reason: null,
146
+ });
147
+ }
148
+ catch (err) {
149
+ sendJson(res, 200, { ...emptyDirectSessionResponse(filters), reason: errorMessage(err) });
150
+ }
151
+ }
152
+ function emptyDirectSessionResponse(filters) {
153
+ return {
154
+ sessions: [],
155
+ total: 0,
156
+ returned: 0,
157
+ nextCursor: null,
158
+ next_cursor: null,
159
+ filters: {
160
+ applied: {
161
+ state: filters?.state || null,
162
+ repo_path: filters?.repo_path || null,
163
+ date_from: filters?.date_from || null,
164
+ date_to: filters?.date_to || null,
165
+ },
166
+ options: { repos: [], states: ["active", "archive", "finalized", "audited", "expired"] },
167
+ },
168
+ direct_profile_enabled: config.enableDirectProfile === true,
169
+ reason: null,
170
+ };
171
+ }
172
+ export function handleDirectSessionDetail(res, sessionId) {
173
+ try {
174
+ if (!isValidDirectSessionId(sessionId)) {
175
+ sendJson(res, 400, { error: "Invalid session id" });
176
+ return;
177
+ }
178
+ const sessionsDir = getDirectSessionsDir(config);
179
+ const sessionDir = guardControlPath(join(sessionsDir, sessionId), config.directSessionsDir);
180
+ if (!sessionDir || !existsSync(sessionDir) || !statSync(sessionDir).isDirectory()) {
181
+ sendJson(res, 404, { error: "Direct session not found" });
182
+ return;
183
+ }
184
+ const summary = readDirectSessionSummary(sessionDir, sessionId);
185
+ sendJson(res, 200, {
186
+ session_id: sessionId,
187
+ summary,
188
+ session: readJsonFileSafe(join(sessionDir, "session.json")),
189
+ summary_md: readTextFileSafe(join(sessionDir, "summary.md")),
190
+ diff_patch: readTextFileSafe(join(sessionDir, "diff.patch")),
191
+ audit_json: readJsonFileSafe(join(sessionDir, "audit.json")),
192
+ audit_md: readTextFileSafe(join(sessionDir, "audit.md")),
193
+ changed_files: readJsonFileSafe(join(sessionDir, "changed-files.json")),
194
+ });
195
+ }
196
+ catch (err) {
197
+ sendJson(res, 200, { session_id: sessionId, error: errorMessage(err) });
198
+ }
199
+ }
200
+ export function handleDirectSessionSafeSummary(res, sessionId) {
201
+ try {
202
+ if (!isValidDirectSessionId(sessionId)) {
203
+ sendJson(res, 400, { error: "Invalid session id" });
204
+ return;
205
+ }
206
+ sendJson(res, 200, safeDirectSummary(sessionId, { max_items: 12 }));
207
+ }
208
+ catch (err) {
209
+ sendJson(res, 200, {
210
+ session_id: sessionId,
211
+ error: errorMessage(err),
212
+ large_logs_omitted: true,
213
+ diff_omitted: true,
214
+ });
215
+ }
216
+ }
217
+ /**
218
+ * Finalize a direct session via safeFinalizeDirectSession. Returns the safe
219
+ * summary on success, or { error } on failure with HTTP 200 (fault tolerance:
220
+ * the UI always gets a JSON body it can render).
221
+ */
222
+ export async function handleDirectSessionFinalize(res, sessionId) {
223
+ try {
224
+ if (!isValidDirectSessionId(sessionId)) {
225
+ sendJson(res, 400, { error: "Invalid session id" });
226
+ return;
227
+ }
228
+ const result = await safeFinalizeDirectSession(sessionId, { max_items: 12 });
229
+ recordEvent("direct_session.finalized", { session_id: sessionId });
230
+ sendJson(res, 200, result);
231
+ }
232
+ catch (err) {
233
+ sendJson(res, 200, {
234
+ session_id: sessionId,
235
+ error: errorMessage(err),
236
+ large_logs_omitted: true,
237
+ diff_omitted: true,
238
+ });
239
+ }
240
+ }
241
+ /**
242
+ * Audit a direct session via safeAuditDirectSession. Returns the safe audit
243
+ * result on success, or { error } on failure (HTTP 200 for fault tolerance).
244
+ */
245
+ export function handleDirectSessionAudit(res, sessionId) {
246
+ try {
247
+ if (!isValidDirectSessionId(sessionId)) {
248
+ sendJson(res, 400, { error: "Invalid session id" });
249
+ return;
250
+ }
251
+ const result = safeAuditDirectSession(sessionId, { max_items: 12 });
252
+ recordEvent("direct_session.audited", { session_id: sessionId });
253
+ sendJson(res, 200, result);
254
+ }
255
+ catch (err) {
256
+ sendJson(res, 200, {
257
+ session_id: sessionId,
258
+ error: errorMessage(err),
259
+ large_logs_omitted: true,
260
+ diff_omitted: true,
261
+ });
262
+ }
263
+ }
264
+ /**
265
+ * Hide a direct session from the dashboard list. The session itself is NOT
266
+ * deleted or modified — only the control-center's local
267
+ * hidden-direct-session-ids.json state file is updated. Requires control token
268
+ * (enforced by the POST router).
269
+ */
270
+ export function handleDirectSessionHide(res, sessionId) {
271
+ try {
272
+ if (!isValidDirectSessionId(sessionId)) {
273
+ sendJson(res, 400, { error: "Invalid session id" });
274
+ return;
275
+ }
276
+ const ids = readHiddenDirectSessionIds();
277
+ if (!ids.includes(sessionId)) {
278
+ ids.push(sessionId);
279
+ writeHiddenDirectSessionIds(ids);
280
+ }
281
+ recordEvent("direct_session.hidden", { session_id: sessionId });
282
+ sendJson(res, 200, { ok: true, hidden: sessionId });
283
+ }
284
+ catch (err) {
285
+ sendJson(res, 500, { error: errorMessage(err) });
286
+ }
287
+ }
@@ -0,0 +1,51 @@
1
+ import { type ServerResponse } from "node:http";
2
+ import { type AgentAvailability } from "../../tools/workspace/listAgents.js";
3
+ import { type WatcherStatusSnapshot } from "../../watcherStatus.js";
4
+ import { type RuntimeHealth, type StatusTasks } from "../runtime.js";
5
+ interface Suggestion {
6
+ code: string;
7
+ severity: "info" | "warning" | "error";
8
+ message: string;
9
+ action?: string;
10
+ link?: string;
11
+ }
12
+ interface StatusSnapshotForSuggestions {
13
+ core: RuntimeHealth;
14
+ direct: RuntimeHealth;
15
+ watcher: WatcherStatusSnapshot;
16
+ tunnel: {
17
+ core: Record<string, unknown>;
18
+ direct: Record<string, unknown>;
19
+ };
20
+ agents: AgentAvailability[];
21
+ tasks: StatusTasks;
22
+ direct_profile_enabled: boolean;
23
+ }
24
+ export interface ExperienceStatus {
25
+ readiness: "ready" | "needs_action" | "blocked";
26
+ live_checks: Array<{
27
+ key: string;
28
+ label: string;
29
+ state: "ready" | "needs_action" | "blocked" | "optional";
30
+ detail: string;
31
+ }>;
32
+ historical_summary: {
33
+ stale_tasks: number;
34
+ failed_tasks: number;
35
+ note: string;
36
+ };
37
+ }
38
+ /**
39
+ * Keep current readiness separate from historical evidence. A month-old failed
40
+ * task is useful for audit, but must not make a configured local bridge look
41
+ * unavailable on the first screen.
42
+ */
43
+ export declare function buildExperienceStatus(snapshot: StatusSnapshotForSuggestions): ExperienceStatus;
44
+ export declare function reconcileTunnelStatus(status: Record<string, unknown>, health: RuntimeHealth): Record<string, unknown>;
45
+ export declare function buildSuggestions(s: StatusSnapshotForSuggestions): Suggestion[];
46
+ export declare function handleStatus(res: ServerResponse): Promise<void>;
47
+ export declare function handleControlCenterStatus(res: ServerResponse): void;
48
+ export declare function handleEvents(res: ServerResponse, limit: number): void;
49
+ export declare function handleTunnelUiUrl(res: ServerResponse): void;
50
+ export declare function handleDiagnostics(res: ServerResponse): Promise<void>;
51
+ export {};
@@ -0,0 +1,307 @@
1
+ /**
2
+ * Control Center routes — runtime status, events, diagnostics, tunnel URLs.
3
+ *
4
+ * `handleStatus` is the dashboard's primary poll endpoint: it fans out health
5
+ * probes, watcher/task snapshots, and suggestion generation, and records
6
+ * observed state-change events (core/direct/watcher/task transitions) into the
7
+ * activity timeline. The remaining endpoints expose the control-center status
8
+ * file, the event timeline, diagnostics (redacted), and tunnel UI URLs.
9
+ */
10
+ import { existsSync } from "node:fs";
11
+ import { PATCHWARDEN_VERSION, TOOL_SCHEMA_EPOCH } from "../../version.js";
12
+ import { redactSensitiveValue } from "../../security/contentRedaction.js";
13
+ import { listAgentsSafe, listTasksForStatus, probeRuntimeHealth, readEvents, recordEvent, readToolManifest, readTunnelStatus, readTunnelUrl, readWatcherStatusSafe, resolveWorkspaceRootSafe, } from "../runtime.js";
14
+ import { findTunnelClientExecutable } from "./process.js";
15
+ import { config, configIdentitySha256, controlCenterStatusPath, CORE_BASE_URL, DIRECT_BASE_URL, errorMessage, readJsonFileSafe, sendJson, } from "../shared.js";
16
+ /**
17
+ * Keep current readiness separate from historical evidence. A month-old failed
18
+ * task is useful for audit, but must not make a configured local bridge look
19
+ * unavailable on the first screen.
20
+ */
21
+ export function buildExperienceStatus(snapshot) {
22
+ const coreReady = snapshot.core.available && snapshot.tunnel.core.ready === true;
23
+ const watcherReady = snapshot.watcher.status === "healthy";
24
+ const agentsReady = snapshot.agents.some((agent) => agent.available);
25
+ const directEnabled = snapshot.direct_profile_enabled;
26
+ const directReady = snapshot.direct.available && snapshot.tunnel.direct.ready === true;
27
+ const staleTasks = snapshot.tasks.stale;
28
+ const failedTasks = snapshot.tasks.tasks.filter((task) => String(task.status || "").includes("failed")).length;
29
+ const live_checks = [
30
+ { key: "workspace_agents", label: "工作区与 Agent", state: agentsReady ? "ready" : "blocked", detail: agentsReady ? "工作区内至少有一个可用 Agent" : "需要配置工作区或可用 Agent" },
31
+ { key: "core", label: "Core 服务", state: coreReady ? "ready" : "needs_action", detail: coreReady ? "Core 与 Tunnel 已就绪" : "启动 Core 后会继续检查 Tunnel 与 Watcher" },
32
+ { key: "watcher", label: "任务监视器", state: watcherReady ? "ready" : "needs_action", detail: watcherReady ? "Watcher 心跳正常" : "Core 启动后 Watcher 将处理排队任务" },
33
+ { key: "direct", label: "Direct 编辑", state: directEnabled ? (directReady ? "ready" : "needs_action") : "optional", detail: directEnabled ? (directReady ? "Direct 已就绪" : "已启用但尚未就绪") : "可选高级能力,未启用不会影响 Core" },
34
+ ];
35
+ const readiness = !agentsReady
36
+ ? "blocked"
37
+ : coreReady && watcherReady && (!directEnabled || directReady)
38
+ ? "ready"
39
+ : "needs_action";
40
+ return {
41
+ readiness,
42
+ live_checks,
43
+ historical_summary: {
44
+ stale_tasks: staleTasks,
45
+ failed_tasks: failedTasks,
46
+ note: "历史任务与审计记录不会影响当前实时就绪状态。",
47
+ },
48
+ };
49
+ }
50
+ export function reconcileTunnelStatus(status, health) {
51
+ if (!health.available)
52
+ return status;
53
+ return {
54
+ ...status,
55
+ observed: true,
56
+ status: "running",
57
+ ready: true,
58
+ reason_code: "health_endpoint_ready",
59
+ health_observed: true,
60
+ };
61
+ }
62
+ export function buildSuggestions(s) {
63
+ const out = [];
64
+ if (!s.core.available) {
65
+ out.push({
66
+ code: "core_stopped",
67
+ severity: "warning",
68
+ message: "Core 未运行,建议启动 Core profile",
69
+ action: "/api/core/start",
70
+ });
71
+ }
72
+ if (s.direct_profile_enabled && !s.direct.available) {
73
+ out.push({
74
+ code: "direct_stopped",
75
+ severity: "warning",
76
+ message: "Direct 未运行,建议启动 Direct profile",
77
+ action: "/api/direct/start",
78
+ });
79
+ }
80
+ if (s.watcher.status === "degraded" || s.watcher.status === "stale" || s.watcher.status === "unreadable") {
81
+ out.push({
82
+ code: "watcher_stale",
83
+ severity: "error",
84
+ message: "Watcher 处于 " + s.watcher.status + " 状态,建议重启 Core",
85
+ action: "/api/core/restart",
86
+ });
87
+ }
88
+ if (s.tasks.stale > 0) {
89
+ out.push({
90
+ code: "stale_task",
91
+ severity: "warning",
92
+ message: "存在 " + s.tasks.stale + " 个 stale 任务,建议查看并 reconcile",
93
+ link: "/pages/tasks.html?filter=stale",
94
+ });
95
+ }
96
+ const coreTunnelReady = !!(s.tunnel.core && s.tunnel.core.ready);
97
+ const directTunnelReady = !!(s.tunnel.direct && s.tunnel.direct.ready);
98
+ if (!coreTunnelReady || (s.direct_profile_enabled && !directTunnelReady)) {
99
+ out.push({
100
+ code: "tunnel_not_ready",
101
+ severity: "warning",
102
+ message: "Tunnel 未就绪,建议启动 profile 或检查代理",
103
+ action: "/api/start-all",
104
+ });
105
+ }
106
+ const missingAgents = s.agents.filter((a) => !a.available);
107
+ if (missingAgents.length > 0) {
108
+ out.push({
109
+ code: "agent_missing",
110
+ severity: "info",
111
+ message: "Agent 未就绪:" + missingAgents.map((a) => a.name).join(", ") + "(请检查对应 CLI 安装与路径)",
112
+ });
113
+ }
114
+ return out;
115
+ }
116
+ let lastStatusDigest = null;
117
+ function buildStatusDigest(s) {
118
+ const task_statuses = {};
119
+ for (const t of s.tasks.tasks) {
120
+ const entry = t;
121
+ task_statuses[entry.task_id] = entry.status;
122
+ }
123
+ return {
124
+ core_available: s.core.available,
125
+ direct_available: s.direct.available,
126
+ watcher_status: s.watcher.status,
127
+ task_statuses,
128
+ };
129
+ }
130
+ function diffAndRecordEvents(prev, curr) {
131
+ if (prev.core_available !== curr.core_available) {
132
+ recordEvent("core.status_changed", { from: prev.core_available, to: curr.core_available });
133
+ }
134
+ if (prev.direct_available !== curr.direct_available) {
135
+ recordEvent("direct.status_changed", { from: prev.direct_available, to: curr.direct_available });
136
+ }
137
+ if (prev.watcher_status !== curr.watcher_status) {
138
+ recordEvent("watcher.status_changed", { from: prev.watcher_status, to: curr.watcher_status });
139
+ }
140
+ for (const [taskId, newStatus] of Object.entries(curr.task_statuses)) {
141
+ const oldStatus = prev.task_statuses[taskId];
142
+ if (oldStatus && oldStatus !== newStatus) {
143
+ recordEvent("task.status_changed", { task_id: taskId, from: oldStatus, to: newStatus });
144
+ }
145
+ }
146
+ }
147
+ // ── Handlers ──────────────────────────────────────────────────────
148
+ export async function handleStatus(res) {
149
+ try {
150
+ const [coreHealth, directHealth, watcher, tunnelCoreRaw, tunnelDirectRaw, toolsCore, toolsDirect, agents, workspaceRoot, tasks] = await Promise.all([
151
+ probeRuntimeHealth(CORE_BASE_URL).catch((err) => ({
152
+ available: false,
153
+ reason: errorMessage(err),
154
+ healthz: null,
155
+ readyz: null,
156
+ })),
157
+ probeRuntimeHealth(DIRECT_BASE_URL).catch((err) => ({
158
+ available: false,
159
+ reason: errorMessage(err),
160
+ healthz: null,
161
+ readyz: null,
162
+ })),
163
+ Promise.resolve(readWatcherStatusSafe()),
164
+ Promise.resolve(readTunnelStatus(false)),
165
+ Promise.resolve(readTunnelStatus(true)),
166
+ Promise.resolve(readToolManifest(false)),
167
+ Promise.resolve(readToolManifest(true)),
168
+ Promise.resolve(listAgentsSafe()),
169
+ Promise.resolve(resolveWorkspaceRootSafe()),
170
+ Promise.resolve(listTasksForStatus()),
171
+ ]);
172
+ const tunnelCore = reconcileTunnelStatus(tunnelCoreRaw, coreHealth);
173
+ const tunnelDirect = reconcileTunnelStatus(tunnelDirectRaw, directHealth);
174
+ const snapshotForSuggestions = {
175
+ core: coreHealth,
176
+ direct: directHealth,
177
+ watcher,
178
+ tunnel: { core: tunnelCore, direct: tunnelDirect },
179
+ agents,
180
+ tasks,
181
+ direct_profile_enabled: config.enableDirectProfile === true,
182
+ };
183
+ const suggestions = buildSuggestions(snapshotForSuggestions);
184
+ const experience = buildExperienceStatus(snapshotForSuggestions);
185
+ const tunnelClientExe = findTunnelClientExecutable();
186
+ // Diff against the previous poll to record observed state-change events.
187
+ // This is the only place that observes Core/Direct/watcher/task transitions
188
+ // (the control center is otherwise stateless and pull-driven).
189
+ const digest = buildStatusDigest(snapshotForSuggestions);
190
+ if (lastStatusDigest) {
191
+ diffAndRecordEvents(lastStatusDigest, digest);
192
+ }
193
+ lastStatusDigest = digest;
194
+ sendJson(res, 200, {
195
+ core: coreHealth,
196
+ direct: directHealth,
197
+ watcher,
198
+ tunnel: { core: tunnelCore, direct: tunnelDirect },
199
+ tools: { core: toolsCore, direct: toolsDirect },
200
+ agents,
201
+ workspace_root: workspaceRoot,
202
+ tasks,
203
+ suggestions,
204
+ experience,
205
+ direct_profile_enabled: config.enableDirectProfile === true,
206
+ setup: {
207
+ tunnel_client: {
208
+ available: tunnelClientExe !== null,
209
+ path: tunnelClientExe,
210
+ source: config.tunnelClientPath && tunnelClientExe === config.tunnelClientPath ? "config" : "detected",
211
+ },
212
+ workspace_root: workspaceRoot,
213
+ watcher: {
214
+ status: watcher.status,
215
+ available: watcher.available,
216
+ reason: watcher.reason,
217
+ },
218
+ },
219
+ });
220
+ }
221
+ catch (err) {
222
+ sendJson(res, 200, { error: errorMessage(err), partial: true });
223
+ }
224
+ }
225
+ export function handleControlCenterStatus(res) {
226
+ // Public read of the status file (used by tray/launcher to confirm identity).
227
+ if (!existsSync(controlCenterStatusPath)) {
228
+ sendJson(res, 200, { running: false });
229
+ return;
230
+ }
231
+ const data = readJsonFileSafe(controlCenterStatusPath);
232
+ if (!data) {
233
+ sendJson(res, 200, { running: false });
234
+ return;
235
+ }
236
+ sendJson(res, 200, { running: true, ...data });
237
+ }
238
+ export function handleEvents(res, limit) {
239
+ const events = readEvents(limit);
240
+ sendJson(res, 200, {
241
+ events,
242
+ total: events.length,
243
+ limit,
244
+ });
245
+ }
246
+ export function handleTunnelUiUrl(res) {
247
+ sendJson(res, 200, {
248
+ core: readTunnelUrl(false),
249
+ direct: readTunnelUrl(true),
250
+ });
251
+ }
252
+ export async function handleDiagnostics(res) {
253
+ try {
254
+ const watcher = readWatcherStatusSafe();
255
+ const [coreHealth, directHealth] = await Promise.all([
256
+ probeRuntimeHealth(CORE_BASE_URL),
257
+ probeRuntimeHealth(DIRECT_BASE_URL),
258
+ ]);
259
+ const tunnelCore = reconcileTunnelStatus(readTunnelStatus(false), coreHealth);
260
+ const tunnelDirect = reconcileTunnelStatus(readTunnelStatus(true), directHealth);
261
+ const toolsCore = readToolManifest(false);
262
+ const toolsDirect = readToolManifest(true);
263
+ const agents = listAgentsSafe();
264
+ let workspaceRoot = null;
265
+ try {
266
+ workspaceRoot = resolveWorkspaceRootSafe();
267
+ }
268
+ catch {
269
+ workspaceRoot = null;
270
+ }
271
+ // Recent failures: last 5 failed tasks (status contains "failed")
272
+ let recentFailures = [];
273
+ try {
274
+ const result = listTasksForStatus();
275
+ recentFailures = result.tasks
276
+ .filter((t) => typeof t.status === "string" && t.status.includes("failed"))
277
+ .slice(0, 5)
278
+ .map((t) => ({ task_id: t.task_id, status: t.status }));
279
+ }
280
+ catch {
281
+ recentFailures = [];
282
+ }
283
+ const coreReady = !!(tunnelCore && tunnelCore.ready);
284
+ const directReady = !!(tunnelDirect && tunnelDirect.ready);
285
+ const diagnostics = {
286
+ server_version: PATCHWARDEN_VERSION,
287
+ schema_epoch: TOOL_SCHEMA_EPOCH,
288
+ tool_manifest_sha256: toolsCore.tool_manifest_sha256,
289
+ watcher_status: watcher.status,
290
+ tunnel_core_ready: coreReady,
291
+ tunnel_direct_ready: directReady,
292
+ core_tool_count: toolsCore.tool_count,
293
+ direct_tool_count: toolsDirect.tool_count,
294
+ agent_status: agents.map((a) => ({ name: a.name, available: a.available })),
295
+ workspace_root: workspaceRoot,
296
+ recent_failures: recentFailures,
297
+ direct_profile_enabled: config.enableDirectProfile ?? false,
298
+ config_identity_sha256: configIdentitySha256,
299
+ };
300
+ // Redact any sensitive content that may have leaked into string fields.
301
+ const redacted = redactSensitiveValue(diagnostics);
302
+ sendJson(res, 200, redacted.value);
303
+ }
304
+ catch (err) {
305
+ sendJson(res, 200, { error: errorMessage(err) });
306
+ }
307
+ }
@@ -0,0 +1,21 @@
1
+ import { type ServerResponse } from "node:http";
2
+ /**
3
+ * Reconcile a stale task. Does NOT delete the task. Reads the task files,
4
+ * decides whether it is safe to mark the task as stale/archived, writes a
5
+ * reconcile record, and (when safe) annotates status.json with reconcile
6
+ * metadata. The task status enum is never changed — only metadata is added.
7
+ */
8
+ export declare function handleReconcile(res: ServerResponse, taskId: string): void;
9
+ /**
10
+ * Run audit_task for a task. Only safe to delegate when the task directory
11
+ * exists and the task is in a terminal state (auditing a running task mid-flight
12
+ * would race with the watcher writing artifacts).
13
+ */
14
+ export declare function handleTaskAudit(res: ServerResponse, taskId: string): void;
15
+ export declare function handleOpenTaskFolder(res: ServerResponse, taskId: string): void;
16
+ /**
17
+ * Hide a stale task from the dashboard's stale-task view. The task itself is
18
+ * NOT deleted or modified — only the control-center's local hidden-stale-ids
19
+ * state file is updated. Requires control token (enforced by the POST router).
20
+ */
21
+ export declare function handleHideStale(res: ServerResponse, taskId: string): void;