patchwarden 1.5.1 → 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 (573) hide show
  1. package/PatchWarden.cmd +32 -2
  2. package/README.en.md +152 -25
  3. package/README.md +120 -23
  4. package/dist/assessments/agentAssessor.js +21 -12
  5. package/dist/assessments/assessmentStore.js +42 -34
  6. package/dist/config.d.ts +15 -0
  7. package/dist/config.js +66 -13
  8. package/dist/control/fileManager.d.ts +2 -0
  9. package/dist/control/fileManager.js +39 -0
  10. package/dist/control/middleware/auth.d.ts +6 -0
  11. package/dist/control/middleware/auth.js +14 -5
  12. package/dist/control/middleware/static.js +16 -3
  13. package/dist/control/routeTable.d.ts +24 -0
  14. package/dist/control/routeTable.js +263 -0
  15. package/dist/control/routes/audit.js +17 -7
  16. package/dist/control/routes/evidence.js +1 -1
  17. package/dist/control/routes/lineage.js +1 -1
  18. package/dist/control/routes/process.d.ts +7 -0
  19. package/dist/control/routes/process.js +329 -42
  20. package/dist/control/routes/sessions.d.ts +9 -1
  21. package/dist/control/routes/sessions.js +84 -21
  22. package/dist/control/routes/status.d.ts +46 -1
  23. package/dist/control/routes/status.js +72 -15
  24. package/dist/control/routes/taskActions.js +24 -60
  25. package/dist/control/routes/tasks.d.ts +4 -0
  26. package/dist/control/routes/tasks.js +61 -62
  27. package/dist/control/routes/workspace.js +50 -23
  28. package/dist/control/runtime.d.ts +3 -2
  29. package/dist/control/runtime.js +63 -30
  30. package/dist/control/server.js +36 -365
  31. package/dist/control/shared.d.ts +25 -1
  32. package/dist/control/shared.js +64 -11
  33. package/dist/direct/directAudit.js +234 -225
  34. package/dist/direct/directGuards.d.ts +2 -1
  35. package/dist/direct/directGuards.js +42 -29
  36. package/dist/direct/directPatch.d.ts +6 -1
  37. package/dist/direct/directPatch.js +55 -8
  38. package/dist/direct/directSessionStore.d.ts +16 -6
  39. package/dist/direct/directSessionStore.js +127 -35
  40. package/dist/direct/directVerification.js +8 -16
  41. package/dist/doctor.js +62 -52
  42. package/dist/goal/acceptanceEngine.d.ts +1 -1
  43. package/dist/goal/goalProgress.js +75 -69
  44. package/dist/goal/goalReport.js +5 -12
  45. package/dist/goal/goalStatus.d.ts +2 -0
  46. package/dist/goal/goalStatus.js +10 -3
  47. package/dist/goal/goalStore.d.ts +9 -2
  48. package/dist/goal/goalStore.js +152 -45
  49. package/dist/goal/handoffExport.js +6 -6
  50. package/dist/goal/specKitImport.js +97 -96
  51. package/dist/goal/subgoalSync.js +11 -9
  52. package/dist/goal/worktreeManager.d.ts +2 -0
  53. package/dist/goal/worktreeManager.js +209 -136
  54. package/dist/httpServer.js +111 -32
  55. package/dist/logging.js +5 -3
  56. package/dist/policy/projectPolicy.js +5 -2
  57. package/dist/release/releaseGate.d.ts +1 -1
  58. package/dist/release/releaseGate.js +78 -25
  59. package/dist/runner/agentInvocation.d.ts +14 -0
  60. package/dist/runner/agentInvocation.js +96 -4
  61. package/dist/runner/changeCapture.d.ts +13 -5
  62. package/dist/runner/changeCapture.js +243 -61
  63. package/dist/runner/postTaskCleanup.js +48 -19
  64. package/dist/runner/processSecurity.d.ts +56 -0
  65. package/dist/runner/processSecurity.js +373 -0
  66. package/dist/runner/runTask.d.ts +1 -1
  67. package/dist/runner/runTask.js +115 -87
  68. package/dist/runner/simpleProcess.d.ts +3 -0
  69. package/dist/runner/simpleProcess.js +50 -36
  70. package/dist/{taskProgress.d.ts → runner/taskProgress.d.ts} +1 -1
  71. package/dist/{taskProgress.js → runner/taskProgress.js} +2 -2
  72. package/dist/{taskRuntime.d.ts → runner/taskRuntime.d.ts} +1 -1
  73. package/dist/{taskRuntime.js → runner/taskRuntime.js} +3 -2
  74. package/dist/runner/taskStatusStore.d.ts +11 -0
  75. package/dist/runner/taskStatusStore.js +24 -0
  76. package/dist/runner/watch.d.ts +14 -1
  77. package/dist/runner/watch.js +256 -48
  78. package/dist/security/contentRedaction.js +2 -2
  79. package/dist/security/discoveryTokenStore.d.ts +3 -2
  80. package/dist/security/discoveryTokenStore.js +17 -2
  81. package/dist/security/loopbackHost.d.ts +2 -0
  82. package/dist/security/loopbackHost.js +16 -0
  83. package/dist/security/pathGuard.js +32 -17
  84. package/dist/security/planGuard.js +2 -1
  85. package/dist/security/runtimeGuard.js +9 -6
  86. package/dist/security/secretComparison.d.ts +3 -0
  87. package/dist/security/secretComparison.js +11 -0
  88. package/dist/security/sensitiveGuard.d.ts +5 -0
  89. package/dist/security/sensitiveGuard.js +27 -8
  90. package/dist/security/toolInvocationGuard.d.ts +2 -2
  91. package/dist/security/toolInvocationGuard.js +1 -1
  92. package/dist/security/workspaceRootGuard.d.ts +8 -0
  93. package/dist/security/workspaceRootGuard.js +47 -0
  94. package/dist/smoke-test.js +24 -23
  95. package/dist/tools/catalog/index.d.ts +4 -0
  96. package/dist/tools/catalog/index.js +4 -0
  97. package/dist/tools/{toolCatalog.js → catalog/toolCatalog.js} +3 -12
  98. package/dist/tools/{toolRegistry.d.ts → catalog/toolRegistry.d.ts} +3 -3
  99. package/dist/tools/{toolRegistry.js → catalog/toolRegistry.js} +6 -15
  100. package/dist/tools/definitions/toolDefs.d.ts +18 -0
  101. package/dist/tools/definitions/toolDefs.js +1177 -0
  102. package/dist/tools/{auditSession.d.ts → diagnostics/auditSession.d.ts} +1 -1
  103. package/dist/tools/{auditSession.js → diagnostics/auditSession.js} +2 -2
  104. package/dist/tools/{auditTask.d.ts → diagnostics/auditTask.d.ts} +1 -1
  105. package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +99 -29
  106. package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +6 -6
  107. package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +7 -7
  108. package/dist/tools/diagnostics/index.d.ts +6 -0
  109. package/dist/tools/diagnostics/index.js +6 -0
  110. package/dist/tools/{safeStatus.d.ts → diagnostics/safeStatus.d.ts} +3 -3
  111. package/dist/tools/{safeStatus.js → diagnostics/safeStatus.js} +6 -6
  112. package/dist/tools/{safeViews.d.ts → diagnostics/safeViews.d.ts} +12 -8
  113. package/dist/tools/{safeViews.js → diagnostics/safeViews.js} +23 -13
  114. package/dist/tools/{schemaDriftCheck.d.ts → diagnostics/schemaDriftCheck.d.ts} +1 -1
  115. package/dist/tools/{schemaDriftCheck.js → diagnostics/schemaDriftCheck.js} +2 -2
  116. package/dist/tools/{createDirectSession.js → direct/createDirectSession.js} +6 -6
  117. package/dist/tools/{finalizeDirectSession.d.ts → direct/finalizeDirectSession.d.ts} +1 -1
  118. package/dist/tools/direct/finalizeDirectSession.js +86 -0
  119. package/dist/tools/direct/index.d.ts +3 -0
  120. package/dist/tools/direct/index.js +3 -0
  121. package/dist/tools/{runDirectVerificationBundle.js → direct/runDirectVerificationBundle.js} +2 -2
  122. package/dist/tools/{discoverTools.d.ts → discovery/discoverTools.d.ts} +2 -2
  123. package/dist/tools/{discoverTools.js → discovery/discoverTools.js} +5 -5
  124. package/dist/tools/{explainTool.d.ts → discovery/explainTool.d.ts} +2 -2
  125. package/dist/tools/{explainTool.js → discovery/explainTool.js} +2 -2
  126. package/dist/tools/discovery/index.d.ts +3 -0
  127. package/dist/tools/discovery/index.js +3 -0
  128. package/dist/tools/{invokeDiscoveredTool.d.ts → discovery/invokeDiscoveredTool.d.ts} +2 -2
  129. package/dist/tools/{invokeDiscoveredTool.js → discovery/invokeDiscoveredTool.js} +5 -5
  130. package/dist/tools/dispatch/coreDispatch.js +32 -31
  131. package/dist/tools/dispatch/diagnosticDispatch.d.ts +3 -5
  132. package/dist/tools/dispatch/diagnosticDispatch.js +70 -68
  133. package/dist/tools/dispatch/directDispatch.js +11 -10
  134. package/dist/tools/dispatch/goalDispatch.js +5 -4
  135. package/dist/tools/dispatch/releaseDispatch.js +3 -2
  136. package/dist/tools/dispatch/validation.d.ts +7 -0
  137. package/dist/tools/dispatch/validation.js +71 -0
  138. package/dist/tools/{getPlan.js → goals/getPlan.js} +4 -4
  139. package/dist/tools/{goalSubgoalTask.d.ts → goals/goalSubgoalTask.d.ts} +1 -1
  140. package/dist/tools/goals/goalSubgoalTask.js +118 -0
  141. package/dist/tools/goals/index.d.ts +3 -0
  142. package/dist/tools/goals/index.js +3 -0
  143. package/dist/tools/{savePlan.js → goals/savePlan.js} +28 -8
  144. package/dist/tools/registry.d.ts +7 -11
  145. package/dist/tools/registry.js +17 -1178
  146. package/dist/tools/{checkReleaseGate.d.ts → release/checkReleaseGate.d.ts} +1 -1
  147. package/dist/tools/{checkReleaseGate.js → release/checkReleaseGate.js} +3 -3
  148. package/dist/tools/release/index.d.ts +2 -0
  149. package/dist/tools/release/index.js +2 -0
  150. package/dist/tools/{releaseMode.d.ts → release/releaseMode.d.ts} +2 -2
  151. package/dist/tools/{releaseMode.js → release/releaseMode.js} +46 -26
  152. package/dist/tools/tasks/cancelTask.d.ts +11 -0
  153. package/dist/tools/tasks/cancelTask.js +85 -0
  154. package/dist/tools/{createTask.d.ts → tasks/createTask.d.ts} +10 -2
  155. package/dist/tools/{createTask.js → tasks/createTask.js} +42 -27
  156. package/dist/tools/{diagnoseTask.d.ts → tasks/diagnoseTask.d.ts} +1 -1
  157. package/dist/tools/{diagnoseTask.js → tasks/diagnoseTask.js} +8 -8
  158. package/dist/tools/{evidencePack.js → tasks/evidencePack.js} +34 -22
  159. package/dist/tools/{getTaskFile.js → tasks/getTaskFile.js} +4 -4
  160. package/dist/tools/{getTaskStatus.d.ts → tasks/getTaskStatus.d.ts} +6 -1
  161. package/dist/tools/{getTaskStatus.js → tasks/getTaskStatus.js} +5 -5
  162. package/dist/tools/{getTaskStdoutTail.js → tasks/getTaskStdoutTail.js} +13 -11
  163. package/dist/tools/{getTaskSummary.js → tasks/getTaskSummary.js} +33 -26
  164. package/dist/tools/tasks/index.d.ts +18 -0
  165. package/dist/tools/tasks/index.js +18 -0
  166. package/dist/tools/tasks/killTask.d.ts +1 -0
  167. package/dist/tools/{listTasks.d.ts → tasks/listTasks.d.ts} +1 -1
  168. package/dist/tools/{listTasks.js → tasks/listTasks.js} +3 -3
  169. package/dist/tools/{reconcileTasks.d.ts → tasks/reconcileTasks.d.ts} +1 -1
  170. package/dist/tools/{reconcileTasks.js → tasks/reconcileTasks.js} +64 -66
  171. package/dist/tools/tasks/retryTask.js +60 -0
  172. package/dist/tools/{runTaskLoop.d.ts → tasks/runTaskLoop.d.ts} +6 -6
  173. package/dist/tools/{runTaskLoop.js → tasks/runTaskLoop.js} +58 -30
  174. package/dist/tools/tasks/runVerification.js +38 -0
  175. package/dist/tools/{taskLineage.js → tasks/taskLineage.js} +8 -7
  176. package/dist/tools/{taskOutputs.js → tasks/taskOutputs.js} +17 -16
  177. package/dist/tools/{androidDoctor.js → workspace/androidDoctor.js} +16 -10
  178. package/dist/tools/{applyPatch.d.ts → workspace/applyPatch.d.ts} +1 -1
  179. package/dist/tools/workspace/applyPatch.js +53 -0
  180. package/dist/tools/{discardWorktree.js → workspace/discardWorktree.js} +3 -3
  181. package/dist/tools/workspace/index.d.ts +10 -0
  182. package/dist/tools/workspace/index.js +10 -0
  183. package/dist/tools/{listAgents.d.ts → workspace/listAgents.d.ts} +5 -0
  184. package/dist/tools/{listAgents.js → workspace/listAgents.js} +8 -4
  185. package/dist/tools/{listWorkspace.js → workspace/listWorkspace.js} +3 -3
  186. package/dist/tools/{mergeWorktree.js → workspace/mergeWorktree.js} +3 -3
  187. package/dist/tools/{readWorkspaceFile.js → workspace/readWorkspaceFile.js} +8 -8
  188. package/dist/tools/{recommendAgentForTask.js → workspace/recommendAgentForTask.js} +3 -3
  189. package/dist/tools/{searchWorkspace.js → workspace/searchWorkspace.js} +5 -5
  190. package/dist/tools/{syncFile.d.ts → workspace/syncFile.d.ts} +1 -1
  191. package/dist/tools/workspace/syncFile.js +134 -0
  192. package/dist/utils/atomicFile.d.ts +7 -0
  193. package/dist/utils/atomicFile.js +48 -0
  194. package/dist/utils/boundedFile.d.ts +8 -0
  195. package/dist/utils/boundedFile.js +93 -0
  196. package/dist/utils/lockedJsonFile.d.ts +15 -0
  197. package/dist/utils/lockedJsonFile.js +224 -0
  198. package/dist/utils/platform.d.ts +11 -0
  199. package/dist/utils/platform.js +26 -0
  200. package/dist/utils/stableJson.d.ts +2 -0
  201. package/dist/utils/stableJson.js +12 -0
  202. package/dist/version.d.ts +3 -2
  203. package/dist/version.js +3 -2
  204. package/dist/watcherStatus.d.ts +2 -2
  205. package/dist/watcherStatus.js +18 -0
  206. package/docs/CODE_WIKI.md +808 -143
  207. package/docs/architecture.md +15 -55
  208. package/docs/assets/patchwarden-desktop-onboarding.png +0 -0
  209. package/docs/control-center/README.md +4 -4
  210. package/docs/dashboard-overview.md +4 -4
  211. package/docs/desktop-app.md +175 -0
  212. package/docs/evidence-pack-schema.md +3 -3
  213. package/docs/open-source-application.md +17 -11
  214. package/docs/release-checklist.md +1 -1
  215. package/docs/release-evidence.md +39 -29
  216. package/docs/task-safe-review-workflow.md +5 -5
  217. package/docs/threat-model.md +19 -1
  218. package/examples/config.example.json +4 -2
  219. package/package.json +27 -10
  220. package/scripts/README.md +15 -6
  221. package/scripts/build.js +11 -0
  222. package/scripts/checks/brand-check.js +3 -0
  223. package/scripts/checks/build-output-check.js +37 -0
  224. package/scripts/checks/control-center-smoke.js +91 -6
  225. package/scripts/checks/control-smoke.js +19 -8
  226. package/scripts/checks/http-mcp-smoke.js +82 -4
  227. package/scripts/checks/lifecycle-smoke.js +10 -10
  228. package/scripts/checks/mcp-manifest-check.js +1 -1
  229. package/scripts/checks/mcp-smoke.js +27 -3
  230. package/scripts/checks/package-manifest-check.js +20 -7
  231. package/scripts/checks/unit-tests.js +3 -5
  232. package/scripts/checks/watcher-supervisor-smoke.js +9 -0
  233. package/scripts/control/get-patchwarden-health.ps1 +1 -1
  234. package/scripts/control/manage-patchwarden.ps1 +69 -4
  235. package/scripts/control/provision-patchwarden-tunnel.ps1 +127 -0
  236. package/scripts/control/run-background-supervisor.ps1 +41 -0
  237. package/scripts/control/start-patchwarden-tunnel.ps1 +126 -32
  238. package/{PatchWarden-Control-Tray.cmd → scripts/launchers/PatchWarden-Control-Tray.cmd} +3 -3
  239. package/scripts/launchers/PatchWarden-Control.cmd +6 -0
  240. package/{PatchWarden-Desktop.cmd → scripts/launchers/PatchWarden-Desktop.cmd} +2 -2
  241. package/scripts/launchers/Restart-PatchWarden-Control.cmd +6 -0
  242. package/{Stop-PatchWarden.cmd → scripts/launchers/Stop-PatchWarden.cmd} +2 -2
  243. package/scripts/lib/clean-generated-output.js +11 -0
  244. package/scripts/lib/file-discovery.js +12 -0
  245. package/scripts/release/desktop-preflight.js +211 -0
  246. package/scripts/release/pack-clean.js +59 -14
  247. package/src/assessments/agentAssessor.ts +26 -12
  248. package/src/assessments/assessmentStore.ts +52 -40
  249. package/src/config.ts +70 -13
  250. package/src/control/fileManager.ts +40 -0
  251. package/src/control/middleware/auth.ts +22 -4
  252. package/src/control/middleware/static.ts +16 -3
  253. package/src/control/routeTable.ts +301 -0
  254. package/src/control/routes/audit.ts +14 -6
  255. package/src/control/routes/evidence.ts +1 -1
  256. package/src/control/routes/lineage.ts +1 -1
  257. package/src/control/routes/process.ts +341 -40
  258. package/src/control/routes/sessions.ts +88 -25
  259. package/src/control/routes/status.ts +97 -17
  260. package/src/control/routes/taskActions.ts +24 -58
  261. package/src/control/routes/tasks.ts +66 -58
  262. package/src/control/routes/workspace.ts +56 -25
  263. package/src/control/runtime.ts +69 -33
  264. package/src/control/server.ts +36 -342
  265. package/src/control/shared.ts +77 -11
  266. package/src/direct/directAudit.ts +17 -9
  267. package/src/direct/directGuards.ts +72 -44
  268. package/src/direct/directPatch.ts +95 -8
  269. package/src/direct/directSessionStore.ts +211 -55
  270. package/src/direct/directVerification.ts +9 -23
  271. package/src/doctor.ts +73 -59
  272. package/src/goal/acceptanceEngine.ts +1 -1
  273. package/src/goal/goalProgress.ts +17 -13
  274. package/src/goal/goalReport.ts +5 -13
  275. package/src/goal/goalStatus.ts +19 -3
  276. package/src/goal/goalStore.ts +241 -54
  277. package/src/goal/handoffExport.ts +6 -6
  278. package/src/goal/specKitImport.ts +12 -12
  279. package/src/goal/subgoalSync.ts +11 -8
  280. package/src/goal/worktreeManager.ts +139 -22
  281. package/src/httpServer.ts +113 -30
  282. package/src/logging.ts +10 -3
  283. package/src/policy/projectPolicy.ts +5 -2
  284. package/src/release/releaseGate.ts +74 -27
  285. package/src/runner/agentInvocation.ts +126 -4
  286. package/src/runner/changeCapture.ts +291 -57
  287. package/src/runner/postTaskCleanup.ts +41 -17
  288. package/src/runner/processSecurity.ts +433 -0
  289. package/src/runner/runTask.ts +138 -91
  290. package/src/runner/simpleProcess.ts +66 -31
  291. package/src/{taskProgress.ts → runner/taskProgress.ts} +3 -3
  292. package/src/{taskRuntime.ts → runner/taskRuntime.ts} +4 -3
  293. package/src/runner/taskStatusStore.ts +54 -0
  294. package/src/runner/watch.ts +281 -51
  295. package/src/security/contentRedaction.ts +2 -2
  296. package/src/security/discoveryTokenStore.ts +18 -4
  297. package/src/security/loopbackHost.ts +19 -0
  298. package/src/security/pathGuard.ts +35 -16
  299. package/src/security/planGuard.ts +2 -1
  300. package/src/security/runtimeGuard.ts +9 -6
  301. package/src/security/secretComparison.ts +13 -0
  302. package/src/security/sensitiveGuard.ts +27 -8
  303. package/src/security/toolInvocationGuard.ts +2 -2
  304. package/src/security/workspaceRootGuard.ts +53 -0
  305. package/src/smoke-test.ts +25 -24
  306. package/src/tools/catalog/index.ts +4 -0
  307. package/src/tools/{toolCatalog.ts → catalog/toolCatalog.ts} +3 -12
  308. package/src/tools/{toolRegistry.ts → catalog/toolRegistry.ts} +7 -16
  309. package/src/tools/definitions/toolDefs.ts +1264 -0
  310. package/src/tools/{auditSession.ts → diagnostics/auditSession.ts} +2 -2
  311. package/src/tools/{auditTask.ts → diagnostics/auditTask.ts} +99 -32
  312. package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +8 -8
  313. package/src/tools/diagnostics/index.ts +6 -0
  314. package/src/tools/{safeStatus.ts → diagnostics/safeStatus.ts} +7 -7
  315. package/src/tools/{safeViews.ts → diagnostics/safeViews.ts} +27 -17
  316. package/src/tools/{schemaDriftCheck.ts → diagnostics/schemaDriftCheck.ts} +3 -3
  317. package/src/tools/{createDirectSession.ts → direct/createDirectSession.ts} +6 -6
  318. package/src/tools/{finalizeDirectSession.ts → direct/finalizeDirectSession.ts} +7 -5
  319. package/src/tools/direct/index.ts +3 -0
  320. package/src/tools/{runDirectVerificationBundle.ts → direct/runDirectVerificationBundle.ts} +2 -2
  321. package/src/tools/{discoverTools.ts → discovery/discoverTools.ts} +6 -6
  322. package/src/tools/{explainTool.ts → discovery/explainTool.ts} +3 -3
  323. package/src/tools/discovery/index.ts +3 -0
  324. package/src/tools/{invokeDiscoveredTool.ts → discovery/invokeDiscoveredTool.ts} +7 -7
  325. package/src/tools/dispatch/coreDispatch.ts +36 -31
  326. package/src/tools/dispatch/diagnosticDispatch.ts +26 -21
  327. package/src/tools/dispatch/directDispatch.ts +11 -10
  328. package/src/tools/dispatch/goalDispatch.ts +5 -4
  329. package/src/tools/dispatch/releaseDispatch.ts +3 -2
  330. package/src/tools/dispatch/validation.ts +78 -0
  331. package/src/tools/{getPlan.ts → goals/getPlan.ts} +4 -4
  332. package/src/tools/{goalSubgoalTask.ts → goals/goalSubgoalTask.ts} +41 -26
  333. package/src/tools/goals/index.ts +3 -0
  334. package/src/tools/{savePlan.ts → goals/savePlan.ts} +38 -8
  335. package/src/tools/registry.ts +17 -1261
  336. package/src/tools/{checkReleaseGate.ts → release/checkReleaseGate.ts} +3 -3
  337. package/src/tools/release/index.ts +2 -0
  338. package/src/tools/{releaseMode.ts → release/releaseMode.ts} +42 -24
  339. package/src/tools/tasks/cancelTask.ts +109 -0
  340. package/src/tools/{createTask.ts → tasks/createTask.ts} +49 -26
  341. package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +8 -8
  342. package/src/tools/{evidencePack.ts → tasks/evidencePack.ts} +36 -22
  343. package/src/tools/{getTaskFile.ts → tasks/getTaskFile.ts} +4 -4
  344. package/src/tools/{getTaskStatus.ts → tasks/getTaskStatus.ts} +7 -5
  345. package/src/tools/{getTaskStdoutTail.ts → tasks/getTaskStdoutTail.ts} +13 -11
  346. package/src/tools/{getTaskSummary.ts → tasks/getTaskSummary.ts} +44 -33
  347. package/src/tools/tasks/index.ts +18 -0
  348. package/src/tools/{listTasks.ts → tasks/listTasks.ts} +4 -4
  349. package/src/tools/{reconcileTasks.ts → tasks/reconcileTasks.ts} +68 -66
  350. package/src/tools/tasks/retryTask.ts +69 -0
  351. package/src/tools/{runTaskLoop.ts → tasks/runTaskLoop.ts} +69 -34
  352. package/src/tools/{runVerification.ts → tasks/runVerification.ts} +6 -3
  353. package/src/tools/{taskLineage.ts → tasks/taskLineage.ts} +8 -7
  354. package/src/tools/{taskOutputs.ts → tasks/taskOutputs.ts} +27 -20
  355. package/src/tools/{androidDoctor.ts → workspace/androidDoctor.ts} +15 -10
  356. package/src/tools/{applyPatch.ts → workspace/applyPatch.ts} +38 -16
  357. package/src/tools/{discardWorktree.ts → workspace/discardWorktree.ts} +3 -3
  358. package/src/tools/workspace/index.ts +10 -0
  359. package/src/tools/{listAgents.ts → workspace/listAgents.ts} +11 -4
  360. package/src/tools/{listWorkspace.ts → workspace/listWorkspace.ts} +3 -3
  361. package/src/tools/{mergeWorktree.ts → workspace/mergeWorktree.ts} +3 -3
  362. package/src/tools/{readWorkspaceFile.ts → workspace/readWorkspaceFile.ts} +8 -8
  363. package/src/tools/{recommendAgentForTask.ts → workspace/recommendAgentForTask.ts} +3 -3
  364. package/src/tools/{searchWorkspace.ts → workspace/searchWorkspace.ts} +5 -5
  365. package/src/tools/workspace/syncFile.ts +292 -0
  366. package/src/utils/atomicFile.ts +58 -0
  367. package/src/utils/boundedFile.ts +116 -0
  368. package/src/utils/lockedJsonFile.ts +251 -0
  369. package/src/utils/platform.ts +31 -0
  370. package/src/utils/stableJson.ts +12 -0
  371. package/src/version.ts +3 -2
  372. package/src/watcherStatus.ts +19 -1
  373. package/ui/desktop-bootstrap.js +11 -0
  374. package/ui/desktop-bridge.js +36 -0
  375. package/ui/desktop.css +160 -0
  376. package/ui/getting-started.js +93 -0
  377. package/ui/i18n.js +484 -0
  378. package/ui/log-parser.js +30 -0
  379. package/ui/pages/audit.html +12 -0
  380. package/ui/pages/dashboard.html +90 -147
  381. package/ui/pages/direct-sessions.html +123 -26
  382. package/ui/pages/getting-started.html +43 -0
  383. package/ui/pages/logs.html +105 -22
  384. package/ui/pages/settings.html +101 -0
  385. package/ui/pages/task-detail.html +12 -0
  386. package/ui/pages/tasks.html +54 -8
  387. package/ui/pages/workspace.html +38 -1
  388. package/ui/settings.js +245 -0
  389. package/PatchWarden-Control.cmd +0 -6
  390. package/Restart-PatchWarden-Control.cmd +0 -6
  391. package/dist/test/unit/acceptance-engine.test.d.ts +0 -1
  392. package/dist/test/unit/acceptance-engine.test.js +0 -228
  393. package/dist/test/unit/agent-router.test.d.ts +0 -1
  394. package/dist/test/unit/agent-router.test.js +0 -287
  395. package/dist/test/unit/android-doctor.test.d.ts +0 -1
  396. package/dist/test/unit/android-doctor.test.js +0 -118
  397. package/dist/test/unit/apply-patch.test.d.ts +0 -1
  398. package/dist/test/unit/apply-patch.test.js +0 -225
  399. package/dist/test/unit/audit-checks.test.d.ts +0 -1
  400. package/dist/test/unit/audit-checks.test.js +0 -350
  401. package/dist/test/unit/chinese-path.test.d.ts +0 -1
  402. package/dist/test/unit/chinese-path.test.js +0 -91
  403. package/dist/test/unit/command-guard.test.d.ts +0 -1
  404. package/dist/test/unit/command-guard.test.js +0 -160
  405. package/dist/test/unit/create-task.test.d.ts +0 -1
  406. package/dist/test/unit/create-task.test.js +0 -197
  407. package/dist/test/unit/diagnose-task.test.d.ts +0 -1
  408. package/dist/test/unit/diagnose-task.test.js +0 -457
  409. package/dist/test/unit/direct-guards.test.d.ts +0 -1
  410. package/dist/test/unit/direct-guards.test.js +0 -328
  411. package/dist/test/unit/discovery-token-store.test.d.ts +0 -1
  412. package/dist/test/unit/discovery-token-store.test.js +0 -139
  413. package/dist/test/unit/evidence-pack.test.d.ts +0 -1
  414. package/dist/test/unit/evidence-pack.test.js +0 -224
  415. package/dist/test/unit/get-task-status.test.d.ts +0 -1
  416. package/dist/test/unit/get-task-status.test.js +0 -174
  417. package/dist/test/unit/get-task-summary.test.d.ts +0 -1
  418. package/dist/test/unit/get-task-summary.test.js +0 -146
  419. package/dist/test/unit/goal-graph.test.d.ts +0 -1
  420. package/dist/test/unit/goal-graph.test.js +0 -298
  421. package/dist/test/unit/goal-progress.test.d.ts +0 -1
  422. package/dist/test/unit/goal-progress.test.js +0 -381
  423. package/dist/test/unit/goal-report.test.d.ts +0 -1
  424. package/dist/test/unit/goal-report.test.js +0 -159
  425. package/dist/test/unit/goal-status.test.d.ts +0 -1
  426. package/dist/test/unit/goal-status.test.js +0 -215
  427. package/dist/test/unit/goal-store.test.d.ts +0 -1
  428. package/dist/test/unit/goal-store.test.js +0 -253
  429. package/dist/test/unit/goal-subgoal-task.test.d.ts +0 -1
  430. package/dist/test/unit/goal-subgoal-task.test.js +0 -55
  431. package/dist/test/unit/goal-tools-registry.test.d.ts +0 -1
  432. package/dist/test/unit/goal-tools-registry.test.js +0 -192
  433. package/dist/test/unit/handoff-export.test.d.ts +0 -1
  434. package/dist/test/unit/handoff-export.test.js +0 -263
  435. package/dist/test/unit/invoke-discovered-tool.test.d.ts +0 -1
  436. package/dist/test/unit/invoke-discovered-tool.test.js +0 -167
  437. package/dist/test/unit/logging.test.d.ts +0 -1
  438. package/dist/test/unit/logging.test.js +0 -397
  439. package/dist/test/unit/path-guard.test.d.ts +0 -1
  440. package/dist/test/unit/path-guard.test.js +0 -133
  441. package/dist/test/unit/post-task-cleanup.test.d.ts +0 -1
  442. package/dist/test/unit/post-task-cleanup.test.js +0 -48
  443. package/dist/test/unit/project-policy-release-mode.test.d.ts +0 -1
  444. package/dist/test/unit/project-policy-release-mode.test.js +0 -125
  445. package/dist/test/unit/reconcile-tasks.test.d.ts +0 -1
  446. package/dist/test/unit/reconcile-tasks.test.js +0 -456
  447. package/dist/test/unit/release-gate.test.d.ts +0 -1
  448. package/dist/test/unit/release-gate.test.js +0 -242
  449. package/dist/test/unit/run-task-loop.test.d.ts +0 -1
  450. package/dist/test/unit/run-task-loop.test.js +0 -380
  451. package/dist/test/unit/safe-status.test.d.ts +0 -1
  452. package/dist/test/unit/safe-status.test.js +0 -165
  453. package/dist/test/unit/safe-views.test.d.ts +0 -1
  454. package/dist/test/unit/safe-views.test.js +0 -171
  455. package/dist/test/unit/schema-drift-check.test.d.ts +0 -1
  456. package/dist/test/unit/schema-drift-check.test.js +0 -176
  457. package/dist/test/unit/sensitive-guard.test.d.ts +0 -1
  458. package/dist/test/unit/sensitive-guard.test.js +0 -104
  459. package/dist/test/unit/spec-kit-import.test.d.ts +0 -1
  460. package/dist/test/unit/spec-kit-import.test.js +0 -341
  461. package/dist/test/unit/subgoal-sync.test.d.ts +0 -1
  462. package/dist/test/unit/subgoal-sync.test.js +0 -183
  463. package/dist/test/unit/sync-file.test.d.ts +0 -1
  464. package/dist/test/unit/sync-file.test.js +0 -154
  465. package/dist/test/unit/tool-invocation-guard.test.d.ts +0 -1
  466. package/dist/test/unit/tool-invocation-guard.test.js +0 -432
  467. package/dist/test/unit/tool-usage-stats.test.d.ts +0 -1
  468. package/dist/test/unit/tool-usage-stats.test.js +0 -300
  469. package/dist/test/unit/toolSearch.test.d.ts +0 -1
  470. package/dist/test/unit/toolSearch.test.js +0 -571
  471. package/dist/test/unit/wait-for-task.test.d.ts +0 -1
  472. package/dist/test/unit/wait-for-task.test.js +0 -144
  473. package/dist/test/unit/watcher-status.test.d.ts +0 -1
  474. package/dist/test/unit/watcher-status.test.js +0 -169
  475. package/dist/test/unit/worktree-manager.test.d.ts +0 -1
  476. package/dist/test/unit/worktree-manager.test.js +0 -176
  477. package/dist/tools/applyPatch.js +0 -41
  478. package/dist/tools/cancelTask.d.ts +0 -30
  479. package/dist/tools/cancelTask.js +0 -64
  480. package/dist/tools/finalizeDirectSession.js +0 -84
  481. package/dist/tools/goalSubgoalTask.js +0 -110
  482. package/dist/tools/killTask.d.ts +0 -15
  483. package/dist/tools/retryTask.js +0 -45
  484. package/dist/tools/runVerification.js +0 -36
  485. package/dist/tools/syncFile.js +0 -65
  486. package/docs/control-center/control-center-daily-driver.md +0 -211
  487. package/docs/control-center/control-center-mvp.md +0 -205
  488. package/docs/control-center/control-center-phase2.md +0 -159
  489. package/docs/execution-plan-2026-07-09.md +0 -315
  490. package/docs/release-v0.6.0.md +0 -71
  491. package/docs/release-v0.6.1.md +0 -75
  492. package/docs/release-v0.6.4.md +0 -45
  493. package/docs/roadmap-execution-and-acceptance.md +0 -365
  494. package/src/test/unit/acceptance-engine.test.ts +0 -261
  495. package/src/test/unit/agent-router.test.ts +0 -342
  496. package/src/test/unit/android-doctor.test.ts +0 -158
  497. package/src/test/unit/apply-patch.test.ts +0 -293
  498. package/src/test/unit/audit-checks.test.ts +0 -567
  499. package/src/test/unit/chinese-path.test.ts +0 -106
  500. package/src/test/unit/command-guard.test.ts +0 -221
  501. package/src/test/unit/create-task.test.ts +0 -255
  502. package/src/test/unit/diagnose-task.test.ts +0 -544
  503. package/src/test/unit/direct-guards.test.ts +0 -467
  504. package/src/test/unit/discovery-token-store.test.ts +0 -181
  505. package/src/test/unit/evidence-pack.test.ts +0 -251
  506. package/src/test/unit/get-task-status.test.ts +0 -203
  507. package/src/test/unit/get-task-summary.test.ts +0 -173
  508. package/src/test/unit/goal-graph.test.ts +0 -347
  509. package/src/test/unit/goal-progress.test.ts +0 -538
  510. package/src/test/unit/goal-report.test.ts +0 -189
  511. package/src/test/unit/goal-status.test.ts +0 -270
  512. package/src/test/unit/goal-store.test.ts +0 -318
  513. package/src/test/unit/goal-subgoal-task.test.ts +0 -72
  514. package/src/test/unit/goal-tools-registry.test.ts +0 -245
  515. package/src/test/unit/handoff-export.test.ts +0 -295
  516. package/src/test/unit/invoke-discovered-tool.test.ts +0 -216
  517. package/src/test/unit/logging.test.ts +0 -497
  518. package/src/test/unit/path-guard.test.ts +0 -180
  519. package/src/test/unit/post-task-cleanup.test.ts +0 -53
  520. package/src/test/unit/project-policy-release-mode.test.ts +0 -156
  521. package/src/test/unit/reconcile-tasks.test.ts +0 -551
  522. package/src/test/unit/release-gate.test.ts +0 -314
  523. package/src/test/unit/run-task-loop.test.ts +0 -425
  524. package/src/test/unit/safe-status.test.ts +0 -187
  525. package/src/test/unit/safe-views.test.ts +0 -184
  526. package/src/test/unit/schema-drift-check.test.ts +0 -260
  527. package/src/test/unit/sensitive-guard.test.ts +0 -124
  528. package/src/test/unit/spec-kit-import.test.ts +0 -429
  529. package/src/test/unit/subgoal-sync.test.ts +0 -236
  530. package/src/test/unit/sync-file.test.ts +0 -231
  531. package/src/test/unit/tool-invocation-guard.test.ts +0 -542
  532. package/src/test/unit/tool-usage-stats.test.ts +0 -384
  533. package/src/test/unit/toolSearch.test.ts +0 -756
  534. package/src/test/unit/wait-for-task.test.ts +0 -176
  535. package/src/test/unit/watcher-status.test.ts +0 -190
  536. package/src/test/unit/worktree-manager.test.ts +0 -247
  537. package/src/tools/cancelTask.ts +0 -70
  538. package/src/tools/retryTask.ts +0 -53
  539. package/src/tools/syncFile.ts +0 -122
  540. /package/dist/tools/{toolCatalog.d.ts → catalog/toolCatalog.d.ts} +0 -0
  541. /package/dist/tools/{toolSearch.d.ts → catalog/toolSearch.d.ts} +0 -0
  542. /package/dist/tools/{toolSearch.js → catalog/toolSearch.js} +0 -0
  543. /package/dist/tools/{toolUsageStats.d.ts → catalog/toolUsageStats.d.ts} +0 -0
  544. /package/dist/tools/{toolUsageStats.js → catalog/toolUsageStats.js} +0 -0
  545. /package/dist/tools/{createDirectSession.d.ts → direct/createDirectSession.d.ts} +0 -0
  546. /package/dist/tools/{runDirectVerificationBundle.d.ts → direct/runDirectVerificationBundle.d.ts} +0 -0
  547. /package/dist/tools/{getPlan.d.ts → goals/getPlan.d.ts} +0 -0
  548. /package/dist/tools/{savePlan.d.ts → goals/savePlan.d.ts} +0 -0
  549. /package/dist/tools/{evidencePack.d.ts → tasks/evidencePack.d.ts} +0 -0
  550. /package/dist/tools/{getTaskFile.d.ts → tasks/getTaskFile.d.ts} +0 -0
  551. /package/dist/tools/{getTaskProgress.d.ts → tasks/getTaskProgress.d.ts} +0 -0
  552. /package/dist/tools/{getTaskProgress.js → tasks/getTaskProgress.js} +0 -0
  553. /package/dist/tools/{getTaskStdoutTail.d.ts → tasks/getTaskStdoutTail.d.ts} +0 -0
  554. /package/dist/tools/{getTaskSummary.d.ts → tasks/getTaskSummary.d.ts} +0 -0
  555. /package/dist/tools/{killTask.js → tasks/killTask.js} +0 -0
  556. /package/dist/tools/{retryTask.d.ts → tasks/retryTask.d.ts} +0 -0
  557. /package/dist/tools/{runVerification.d.ts → tasks/runVerification.d.ts} +0 -0
  558. /package/dist/tools/{taskLineage.d.ts → tasks/taskLineage.d.ts} +0 -0
  559. /package/dist/tools/{taskOutputs.d.ts → tasks/taskOutputs.d.ts} +0 -0
  560. /package/dist/tools/{waitForTask.d.ts → tasks/waitForTask.d.ts} +0 -0
  561. /package/dist/tools/{waitForTask.js → tasks/waitForTask.js} +0 -0
  562. /package/dist/tools/{androidDoctor.d.ts → workspace/androidDoctor.d.ts} +0 -0
  563. /package/dist/tools/{discardWorktree.d.ts → workspace/discardWorktree.d.ts} +0 -0
  564. /package/dist/tools/{listWorkspace.d.ts → workspace/listWorkspace.d.ts} +0 -0
  565. /package/dist/tools/{mergeWorktree.d.ts → workspace/mergeWorktree.d.ts} +0 -0
  566. /package/dist/tools/{readWorkspaceFile.d.ts → workspace/readWorkspaceFile.d.ts} +0 -0
  567. /package/dist/tools/{recommendAgentForTask.d.ts → workspace/recommendAgentForTask.d.ts} +0 -0
  568. /package/dist/tools/{searchWorkspace.d.ts → workspace/searchWorkspace.d.ts} +0 -0
  569. /package/src/tools/{toolSearch.ts → catalog/toolSearch.ts} +0 -0
  570. /package/src/tools/{toolUsageStats.ts → catalog/toolUsageStats.ts} +0 -0
  571. /package/src/tools/{getTaskProgress.ts → tasks/getTaskProgress.ts} +0 -0
  572. /package/src/tools/{killTask.ts → tasks/killTask.ts} +0 -0
  573. /package/src/tools/{waitForTask.ts → tasks/waitForTask.ts} +0 -0
@@ -1,6 +1,51 @@
1
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[];
2
46
  export declare function handleStatus(res: ServerResponse): Promise<void>;
3
47
  export declare function handleControlCenterStatus(res: ServerResponse): void;
4
48
  export declare function handleEvents(res: ServerResponse, limit: number): void;
5
49
  export declare function handleTunnelUiUrl(res: ServerResponse): void;
6
- export declare function handleDiagnostics(res: ServerResponse): void;
50
+ export declare function handleDiagnostics(res: ServerResponse): Promise<void>;
51
+ export {};
@@ -12,8 +12,54 @@ import { PATCHWARDEN_VERSION, TOOL_SCHEMA_EPOCH } from "../../version.js";
12
12
  import { redactSensitiveValue } from "../../security/contentRedaction.js";
13
13
  import { listAgentsSafe, listTasksForStatus, probeRuntimeHealth, readEvents, recordEvent, readToolManifest, readTunnelStatus, readTunnelUrl, readWatcherStatusSafe, resolveWorkspaceRootSafe, } from "../runtime.js";
14
14
  import { findTunnelClientExecutable } from "./process.js";
15
- import { config, controlCenterStatusPath, CORE_BASE_URL, DEFAULT_TUNNEL_CLIENT_EXE, DIRECT_BASE_URL, errorMessage, readJsonFileSafe, sendJson, } from "../shared.js";
16
- function buildSuggestions(s) {
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) {
17
63
  const out = [];
18
64
  if (!s.core.available) {
19
65
  out.push({
@@ -23,7 +69,7 @@ function buildSuggestions(s) {
23
69
  action: "/api/core/start",
24
70
  });
25
71
  }
26
- if (!s.direct.available) {
72
+ if (s.direct_profile_enabled && !s.direct.available) {
27
73
  out.push({
28
74
  code: "direct_stopped",
29
75
  severity: "warning",
@@ -31,12 +77,12 @@ function buildSuggestions(s) {
31
77
  action: "/api/direct/start",
32
78
  });
33
79
  }
34
- if (s.watcher.status === "stale" || s.watcher.status === "unreadable") {
80
+ if (s.watcher.status === "degraded" || s.watcher.status === "stale" || s.watcher.status === "unreadable") {
35
81
  out.push({
36
82
  code: "watcher_stale",
37
83
  severity: "error",
38
- message: "Watcher 处于 " + s.watcher.status + " 状态,建议 Restart All",
39
- action: "/api/restart-all",
84
+ message: "Watcher 处于 " + s.watcher.status + " 状态,建议重启 Core",
85
+ action: "/api/core/restart",
40
86
  });
41
87
  }
42
88
  if (s.tasks.stale > 0) {
@@ -49,12 +95,12 @@ function buildSuggestions(s) {
49
95
  }
50
96
  const coreTunnelReady = !!(s.tunnel.core && s.tunnel.core.ready);
51
97
  const directTunnelReady = !!(s.tunnel.direct && s.tunnel.direct.ready);
52
- if (!coreTunnelReady || !directTunnelReady) {
98
+ if (!coreTunnelReady || (s.direct_profile_enabled && !directTunnelReady)) {
53
99
  out.push({
54
100
  code: "tunnel_not_ready",
55
101
  severity: "warning",
56
- message: "Tunnel 未就绪,建议重启 profile 或检查代理",
57
- action: "/api/restart-all",
102
+ message: "Tunnel 未就绪,建议启动 profile 或检查代理",
103
+ action: "/api/start-all",
58
104
  });
59
105
  }
60
106
  const missingAgents = s.agents.filter((a) => !a.available);
@@ -62,7 +108,7 @@ function buildSuggestions(s) {
62
108
  out.push({
63
109
  code: "agent_missing",
64
110
  severity: "info",
65
- message: "Agent 未就绪:" + missingAgents.map((a) => a.name).join(", ") + "(请检查 opencode/claude 路径)",
111
+ message: "Agent 未就绪:" + missingAgents.map((a) => a.name).join(", ") + "(请检查对应 CLI 安装与路径)",
66
112
  });
67
113
  }
68
114
  return out;
@@ -101,7 +147,7 @@ function diffAndRecordEvents(prev, curr) {
101
147
  // ── Handlers ──────────────────────────────────────────────────────
102
148
  export async function handleStatus(res) {
103
149
  try {
104
- const [coreHealth, directHealth, watcher, tunnelCore, tunnelDirect, toolsCore, toolsDirect, agents, workspaceRoot, tasks] = await Promise.all([
150
+ const [coreHealth, directHealth, watcher, tunnelCoreRaw, tunnelDirectRaw, toolsCore, toolsDirect, agents, workspaceRoot, tasks] = await Promise.all([
105
151
  probeRuntimeHealth(CORE_BASE_URL).catch((err) => ({
106
152
  available: false,
107
153
  reason: errorMessage(err),
@@ -123,6 +169,8 @@ export async function handleStatus(res) {
123
169
  Promise.resolve(resolveWorkspaceRootSafe()),
124
170
  Promise.resolve(listTasksForStatus()),
125
171
  ]);
172
+ const tunnelCore = reconcileTunnelStatus(tunnelCoreRaw, coreHealth);
173
+ const tunnelDirect = reconcileTunnelStatus(tunnelDirectRaw, directHealth);
126
174
  const snapshotForSuggestions = {
127
175
  core: coreHealth,
128
176
  direct: directHealth,
@@ -130,8 +178,10 @@ export async function handleStatus(res) {
130
178
  tunnel: { core: tunnelCore, direct: tunnelDirect },
131
179
  agents,
132
180
  tasks,
181
+ direct_profile_enabled: config.enableDirectProfile === true,
133
182
  };
134
183
  const suggestions = buildSuggestions(snapshotForSuggestions);
184
+ const experience = buildExperienceStatus(snapshotForSuggestions);
135
185
  const tunnelClientExe = findTunnelClientExecutable();
136
186
  // Diff against the previous poll to record observed state-change events.
137
187
  // This is the only place that observes Core/Direct/watcher/task transitions
@@ -151,11 +201,13 @@ export async function handleStatus(res) {
151
201
  workspace_root: workspaceRoot,
152
202
  tasks,
153
203
  suggestions,
204
+ experience,
205
+ direct_profile_enabled: config.enableDirectProfile === true,
154
206
  setup: {
155
207
  tunnel_client: {
156
208
  available: tunnelClientExe !== null,
157
209
  path: tunnelClientExe,
158
- default_path: DEFAULT_TUNNEL_CLIENT_EXE,
210
+ source: config.tunnelClientPath && tunnelClientExe === config.tunnelClientPath ? "config" : "detected",
159
211
  },
160
212
  workspace_root: workspaceRoot,
161
213
  watcher: {
@@ -197,11 +249,15 @@ export function handleTunnelUiUrl(res) {
197
249
  direct: readTunnelUrl(true),
198
250
  });
199
251
  }
200
- export function handleDiagnostics(res) {
252
+ export async function handleDiagnostics(res) {
201
253
  try {
202
254
  const watcher = readWatcherStatusSafe();
203
- const tunnelCore = readTunnelStatus(false);
204
- const tunnelDirect = readTunnelStatus(true);
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);
205
261
  const toolsCore = readToolManifest(false);
206
262
  const toolsDirect = readToolManifest(true);
207
263
  const agents = listAgentsSafe();
@@ -239,6 +295,7 @@ export function handleDiagnostics(res) {
239
295
  workspace_root: workspaceRoot,
240
296
  recent_failures: recentFailures,
241
297
  direct_profile_enabled: config.enableDirectProfile ?? false,
298
+ config_identity_sha256: configIdentitySha256,
242
299
  };
243
300
  // Redact any sensitive content that may have leaked into string fields.
244
301
  const redacted = redactSensitiveValue(diagnostics);
@@ -7,13 +7,15 @@
7
7
  * dashboard. None of them delete task files; they annotate or launch external
8
8
  * viewers.
9
9
  */
10
- import { existsSync, statSync, writeFileSync } from "node:fs";
10
+ import { existsSync, statSync } from "node:fs";
11
11
  import { join } from "node:path";
12
- import { spawn } from "node:child_process";
13
- import { auditTask } from "../../tools/auditTask.js";
12
+ import { auditTask } from "../../tools/diagnostics/auditTask.js";
14
13
  import { getTasksDir } from "../../config.js";
15
- import { classifyStaleTask, isValidTaskId, readHiddenStaleIds, readWatcherStatusSafe, recordEvent, TERMINAL_TASK_STATUSES, writeHiddenStaleIds, } from "../runtime.js";
16
- import { config, errorMessage, readJsonFileSafe, sendJson } from "../shared.js";
14
+ import { classifyStaleTask, isValidTaskId, readHiddenStaleIds, reconstructTaskEntry, readWatcherStatusSafe, recordEvent, TERMINAL_TASK_STATUSES, writeHiddenStaleIds, } from "../runtime.js";
15
+ import { launchFileManager } from "../fileManager.js";
16
+ import { config, errorMessage, guardControlPath, readJsonFileSafe, sendJson } from "../shared.js";
17
+ import { atomicWriteJsonFileSync } from "../../utils/atomicFile.js";
18
+ import { mutateTaskStatus } from "../../runner/taskStatusStore.js";
17
19
  /**
18
20
  * Reconcile a stale task. Does NOT delete the task. Reads the task files,
19
21
  * decides whether it is safe to mark the task as stale/archived, writes a
@@ -27,8 +29,8 @@ export function handleReconcile(res, taskId) {
27
29
  return;
28
30
  }
29
31
  const tasksDir = getTasksDir(config);
30
- const taskDir = join(tasksDir, taskId);
31
- if (!existsSync(taskDir) || !statSync(taskDir).isDirectory()) {
32
+ const taskDir = guardControlPath(join(tasksDir, taskId), config.tasksDir);
33
+ if (!taskDir || !existsSync(taskDir) || !statSync(taskDir).isDirectory()) {
32
34
  sendJson(res, 404, { error: "Task not found" });
33
35
  return;
34
36
  }
@@ -37,33 +39,7 @@ export function handleReconcile(res, taskId) {
37
39
  const statusData = readJsonFileSafe(statusPath) ?? {};
38
40
  const runtimeData = readJsonFileSafe(runtimePath) ?? {};
39
41
  const watcher = readWatcherStatusSafe();
40
- const VALID_ACCEPTANCE = ["pending", "accepted", "rejected", "needs_fix", "blocked"];
41
- const taskStatus = String(statusData.status || "pending");
42
- const taskAcceptanceStatus = taskStatus === "done_by_agent"
43
- ? (typeof statusData.acceptance_status === "string" && VALID_ACCEPTANCE.includes(statusData.acceptance_status) ? statusData.acceptance_status : "pending")
44
- : null;
45
- const taskEntry = {
46
- task_id: taskId,
47
- plan_id: String(statusData.plan_id || ""),
48
- title: "",
49
- agent: String(statusData.agent || ""),
50
- status: taskStatus,
51
- phase: String(runtimeData.phase || statusData.phase || "queued"),
52
- acceptance_status: taskAcceptanceStatus,
53
- created_at: String(statusData.created_at || ""),
54
- updated_at: String(statusData.updated_at || ""),
55
- workspace_root: String(statusData.workspace_root || config.workspaceRoot),
56
- repo_path: String(statusData.repo_path || "."),
57
- resolved_repo_path: String(statusData.resolved_repo_path || statusData.repo_path || config.workspaceRoot),
58
- test_command: String(statusData.test_command || ""),
59
- verify_commands: Array.isArray(statusData.verify_commands) ? statusData.verify_commands : [],
60
- error: statusData.error ? String(statusData.error) : null,
61
- last_heartbeat_at: String(runtimeData.last_heartbeat_at || statusData.last_heartbeat_at || statusData.updated_at || ""),
62
- current_command: runtimeData.current_command === undefined ? null : String(runtimeData.current_command || "") || null,
63
- timeout_seconds: Number(statusData.timeout_seconds) || config.defaultTaskTimeoutSeconds,
64
- pending_reason: null,
65
- watcher_status: watcher.status,
66
- };
42
+ const taskEntry = reconstructTaskEntry(taskId, statusData, runtimeData, watcher);
67
43
  const cls = classifyStaleTask(taskEntry, watcher);
68
44
  const isTerminal = TERMINAL_TASK_STATUSES.has(taskEntry.status);
69
45
  // Safe to mark stale/archived when:
@@ -104,7 +80,7 @@ export function handleReconcile(res, taskId) {
104
80
  };
105
81
  // Write the reconcile record artifact.
106
82
  try {
107
- writeFileSync(join(taskDir, "reconcile.json"), JSON.stringify(reconcileRecord, null, 2), "utf-8");
83
+ atomicWriteJsonFileSync(join(taskDir, "reconcile.json"), reconcileRecord);
108
84
  }
109
85
  catch (writeErr) {
110
86
  sendJson(res, 500, { error: `Failed to write reconcile record: ${errorMessage(writeErr)}` });
@@ -112,13 +88,18 @@ export function handleReconcile(res, taskId) {
112
88
  }
113
89
  // Annotate status.json with reconcile metadata (do not mutate status enum).
114
90
  if (safe) {
115
- const annotated = {
116
- ...statusData,
91
+ const annotation = {
117
92
  reconcile_state: decision === "marked_archived" ? "archived" : "stale",
118
93
  reconciled_at: reconciledAt,
119
94
  };
120
95
  try {
121
- writeFileSync(statusPath, JSON.stringify(annotated, null, 2), "utf-8");
96
+ mutateTaskStatus(statusPath, (current) => {
97
+ if (current.status !== taskEntry.status) {
98
+ throw new Error("Task status changed while reconcile was being evaluated; retry.");
99
+ }
100
+ const next = { ...current, ...annotation, updated_at: new Date().toISOString() };
101
+ return { next, result: next };
102
+ });
122
103
  }
123
104
  catch (writeErr) {
124
105
  sendJson(res, 500, { error: `Failed to annotate status.json: ${errorMessage(writeErr)}` });
@@ -150,8 +131,8 @@ export function handleTaskAudit(res, taskId) {
150
131
  return;
151
132
  }
152
133
  const tasksDir = getTasksDir(config);
153
- const taskDir = join(tasksDir, taskId);
154
- if (!existsSync(taskDir) || !statSync(taskDir).isDirectory()) {
134
+ const taskDir = guardControlPath(join(tasksDir, taskId), config.tasksDir);
135
+ if (!taskDir || !existsSync(taskDir) || !statSync(taskDir).isDirectory()) {
155
136
  sendJson(res, 404, { error: "Task not found" });
156
137
  return;
157
138
  }
@@ -179,29 +160,12 @@ export function handleOpenTaskFolder(res, taskId) {
179
160
  return;
180
161
  }
181
162
  const tasksDir = getTasksDir(config);
182
- const taskDir = join(tasksDir, taskId);
183
- if (!existsSync(taskDir) || !statSync(taskDir).isDirectory()) {
163
+ const taskDir = guardControlPath(join(tasksDir, taskId), config.tasksDir);
164
+ if (!taskDir || !existsSync(taskDir) || !statSync(taskDir).isDirectory()) {
184
165
  sendJson(res, 404, { error: "Task not found" });
185
166
  return;
186
167
  }
187
- let cmd;
188
- if (process.platform === "win32") {
189
- cmd = "explorer.exe";
190
- }
191
- else if (process.platform === "darwin") {
192
- cmd = "open";
193
- }
194
- else {
195
- cmd = "xdg-open";
196
- }
197
- try {
198
- const child = spawn(cmd, [taskDir], { detached: true, stdio: "ignore" });
199
- child.on("error", () => { });
200
- child.unref();
201
- }
202
- catch {
203
- /* ignore */
204
- }
168
+ launchFileManager(taskDir, tasksDir);
205
169
  sendJson(res, 200, { ok: true, path: taskDir });
206
170
  }
207
171
  catch (err) {
@@ -5,6 +5,10 @@ export interface TaskFilters {
5
5
  acceptance_status?: string;
6
6
  agent?: string;
7
7
  warning_type?: string;
8
+ date_from?: string;
9
+ date_to?: string;
10
+ limit?: number;
11
+ cursor?: string;
8
12
  }
9
13
  export declare function handleTasks(res: ServerResponse, filters?: TaskFilters): void;
10
14
  export declare function deriveStaleReasonCode(staleReasons: string[]): string;
@@ -9,17 +9,18 @@
9
9
  */
10
10
  import { existsSync, statSync } from "node:fs";
11
11
  import { join } from "node:path";
12
- import { listTasks } from "../../tools/listTasks.js";
13
- import { safeAudit, safeDiffSummary, safeResult, safeTestSummary } from "../../tools/safeViews.js";
12
+ import { listTasks } from "../../tools/tasks/listTasks.js";
13
+ import { safeAudit, safeDiffSummary, safeResult, safeTestSummary } from "../../tools/diagnostics/safeViews.js";
14
14
  import { getTasksDir } from "../../config.js";
15
- import { augmentTaskWithStale, classifyStaleTask, fileMtimeIso, isValidTaskId, parseReviewVerdict, readWatcherStatusSafe, } from "../runtime.js";
16
- import { config, errorMessage, readJsonFileSafe, readTextFileSafe, sendJson } from "../shared.js";
15
+ import { augmentTaskWithStale, classifyStaleTask, fileMtimeIso, isValidTaskId, parseReviewVerdict, reconstructTaskEntry, readWatcherStatusSafe, } from "../runtime.js";
16
+ import { config, errorMessage, guardControlPath, readJsonFileSafe, readTextFileSafe, sendJson } from "../shared.js";
17
17
  export function handleTasks(res, filters) {
18
18
  try {
19
19
  const result = listTasks({ limit: 100 });
20
20
  const watcher = result.watcher;
21
21
  const now = Date.now();
22
22
  let augmented = result.tasks.map((t) => augmentTaskWithStale(t, watcher, now));
23
+ const facetSource = augmented.slice();
23
24
  // Apply optional filters from query params.
24
25
  if (filters) {
25
26
  if (filters.repo_path) {
@@ -46,23 +47,59 @@ export function handleTasks(res, filters) {
46
47
  }
47
48
  if (filters.warning_type) {
48
49
  const wt = filters.warning_type;
49
- if (wt === "stale") {
50
+ if (wt === "stale" || wt === "stale_task") {
50
51
  augmented = augmented.filter((t) => t.is_stale);
51
52
  }
52
53
  else if (wt === "error") {
53
54
  augmented = augmented.filter((t) => t.error !== null && t.error !== "");
54
55
  }
56
+ else if (wt === "failed_verification") {
57
+ augmented = augmented.filter((t) => t.status === wt);
58
+ }
55
59
  else {
56
- // Treat as a specific stale_reason token to match against.
57
- augmented = augmented.filter((t) => Array.isArray(t.stale_reasons) && t.stale_reasons.includes(wt));
60
+ augmented = augmented.filter((t) => t.status === wt || t.acceptance_status === wt ||
61
+ (Array.isArray(t.stale_reasons) && t.stale_reasons.includes(wt)));
58
62
  }
59
63
  }
64
+ if (filters.date_from || filters.date_to) {
65
+ const from = filters.date_from ? Date.parse(`${filters.date_from}T00:00:00`) : Number.NEGATIVE_INFINITY;
66
+ const to = filters.date_to ? Date.parse(`${filters.date_to}T23:59:59`) : Number.POSITIVE_INFINITY;
67
+ augmented = augmented.filter((task) => {
68
+ const timestamp = Date.parse(String(task.updated_at || task.created_at || ""));
69
+ return Number.isFinite(timestamp) && timestamp >= from && timestamp <= to;
70
+ });
71
+ }
60
72
  }
73
+ augmented.sort((a, b) => String(b.created_at || "").localeCompare(String(a.created_at || "")) || String(b.task_id).localeCompare(String(a.task_id)));
61
74
  const staleCount = augmented.filter((t) => t.is_stale).length;
75
+ const limit = Math.max(10, Math.min(filters?.limit || 25, 100));
76
+ const offset = filters?.cursor && /^\d+$/.test(filters.cursor) ? Number(filters.cursor) : 0;
77
+ const page = augmented.slice(offset, offset + limit);
78
+ const nextCursor = offset + page.length < augmented.length ? String(offset + page.length) : null;
79
+ const facets = {
80
+ repos: [...new Set(facetSource.map((task) => String(task.repo_path || ".")))].sort(),
81
+ statuses: [...new Set(facetSource.map((task) => String(task.status || "unknown")))].sort(),
82
+ agents: [...new Set(facetSource.map((task) => String(task.agent || "unknown")))].sort(),
83
+ };
62
84
  sendJson(res, 200, {
63
- tasks: augmented,
85
+ tasks: page,
64
86
  total: augmented.length,
65
- returned: augmented.length,
87
+ returned: page.length,
88
+ nextCursor,
89
+ next_cursor: nextCursor,
90
+ filters: {
91
+ applied: {
92
+ repo_path: filters?.repo_path || null,
93
+ status: filters?.status || null,
94
+ acceptance_status: filters?.acceptance_status || null,
95
+ agent: filters?.agent || null,
96
+ warning_type: filters?.warning_type || null,
97
+ date_from: filters?.date_from || null,
98
+ date_to: filters?.date_to || null,
99
+ },
100
+ options: facets,
101
+ },
102
+ facets,
66
103
  watcher,
67
104
  stale_count: staleCount,
68
105
  });
@@ -72,6 +109,10 @@ export function handleTasks(res, filters) {
72
109
  tasks: [],
73
110
  total: 0,
74
111
  returned: 0,
112
+ nextCursor: null,
113
+ next_cursor: null,
114
+ filters: { applied: {}, options: { repos: [], statuses: [], agents: [] } },
115
+ facets: { repos: [], statuses: [], agents: [] },
75
116
  watcher: null,
76
117
  stale_count: 0,
77
118
  error: errorMessage(err),
@@ -140,9 +181,13 @@ export function handleStaleTasks(res) {
140
181
  }
141
182
  export function handleTaskDetail(res, taskId) {
142
183
  try {
184
+ if (!isValidTaskId(taskId)) {
185
+ sendJson(res, 400, { error: "Invalid task id" });
186
+ return;
187
+ }
143
188
  const tasksDir = getTasksDir(config);
144
- const taskDir = join(tasksDir, taskId);
145
- if (!existsSync(taskDir) || !statSync(taskDir).isDirectory()) {
189
+ const taskDir = guardControlPath(join(tasksDir, taskId), config.tasksDir);
190
+ if (!taskDir || !existsSync(taskDir) || !statSync(taskDir).isDirectory()) {
146
191
  sendJson(res, 404, { error: "Task not found" });
147
192
  return;
148
193
  }
@@ -187,33 +232,7 @@ export function handleTaskDetail(res, taskId) {
187
232
  let stale = null;
188
233
  if (statusData) {
189
234
  const watcher = readWatcherStatusSafe();
190
- const VALID_ACCEPTANCE2 = ["pending", "accepted", "rejected", "needs_fix", "blocked"];
191
- const taskStatus2 = String(statusData.status || "pending");
192
- const taskAcceptanceStatus2 = taskStatus2 === "done_by_agent"
193
- ? (typeof statusData.acceptance_status === "string" && VALID_ACCEPTANCE2.includes(statusData.acceptance_status) ? statusData.acceptance_status : "pending")
194
- : null;
195
- const entry = {
196
- task_id: taskId,
197
- plan_id: String(statusData.plan_id || ""),
198
- title: "",
199
- agent: String(statusData.agent || ""),
200
- status: taskStatus2,
201
- phase: String(runtimeData?.phase || statusData.phase || "queued"),
202
- acceptance_status: taskAcceptanceStatus2,
203
- created_at: String(statusData.created_at || ""),
204
- updated_at: String(statusData.updated_at || ""),
205
- workspace_root: String(statusData.workspace_root || config.workspaceRoot),
206
- repo_path: String(statusData.repo_path || "."),
207
- resolved_repo_path: String(statusData.resolved_repo_path || statusData.repo_path || config.workspaceRoot),
208
- test_command: String(statusData.test_command || ""),
209
- verify_commands: Array.isArray(statusData.verify_commands) ? statusData.verify_commands : [],
210
- error: statusData.error ? String(statusData.error) : null,
211
- last_heartbeat_at: String(runtimeData?.last_heartbeat_at || statusData.last_heartbeat_at || statusData.updated_at || ""),
212
- current_command: runtimeData?.current_command === undefined ? null : String(runtimeData.current_command || "") || null,
213
- timeout_seconds: Number(statusData.timeout_seconds) || config.defaultTaskTimeoutSeconds,
214
- pending_reason: null,
215
- watcher_status: watcher.status,
216
- };
235
+ const entry = reconstructTaskEntry(taskId, statusData, runtimeData ?? {}, watcher);
217
236
  stale = classifyStaleTask(entry, watcher);
218
237
  }
219
238
  sendJson(res, 200, {
@@ -246,12 +265,7 @@ export function handleTaskSafeResult(res, taskId) {
246
265
  sendJson(res, 400, { error: "Invalid task id" });
247
266
  return;
248
267
  }
249
- try {
250
- sendJson(res, 200, safeResult(taskId, { max_items: 12 }));
251
- }
252
- catch (err) {
253
- sendJson(res, 200, { task_id: taskId, error: errorMessage(err) });
254
- }
268
+ sendJson(res, 200, safeResult(taskId, { max_items: 12 }));
255
269
  }
256
270
  catch (err) {
257
271
  sendJson(res, 200, { task_id: taskId, error: errorMessage(err) });
@@ -263,12 +277,7 @@ export function handleTaskSafeAudit(res, taskId) {
263
277
  sendJson(res, 400, { error: "Invalid task id" });
264
278
  return;
265
279
  }
266
- try {
267
- sendJson(res, 200, safeAudit(taskId, { max_items: 12 }));
268
- }
269
- catch (err) {
270
- sendJson(res, 200, { task_id: taskId, error: errorMessage(err) });
271
- }
280
+ sendJson(res, 200, safeAudit(taskId, { max_items: 12 }));
272
281
  }
273
282
  catch (err) {
274
283
  sendJson(res, 200, { task_id: taskId, error: errorMessage(err) });
@@ -280,12 +289,7 @@ export function handleTaskSafeTestSummary(res, taskId) {
280
289
  sendJson(res, 400, { error: "Invalid task id" });
281
290
  return;
282
291
  }
283
- try {
284
- sendJson(res, 200, safeTestSummary(taskId));
285
- }
286
- catch (err) {
287
- sendJson(res, 200, { task_id: taskId, error: errorMessage(err) });
288
- }
292
+ sendJson(res, 200, safeTestSummary(taskId));
289
293
  }
290
294
  catch (err) {
291
295
  sendJson(res, 200, { task_id: taskId, error: errorMessage(err) });
@@ -297,12 +301,7 @@ export function handleTaskSafeDiffSummary(res, taskId) {
297
301
  sendJson(res, 400, { error: "Invalid task id" });
298
302
  return;
299
303
  }
300
- try {
301
- sendJson(res, 200, safeDiffSummary(taskId, { max_items: 12 }));
302
- }
303
- catch (err) {
304
- sendJson(res, 200, { task_id: taskId, error: errorMessage(err) });
305
- }
304
+ sendJson(res, 200, safeDiffSummary(taskId, { max_items: 12 }));
306
305
  }
307
306
  catch (err) {
308
307
  sendJson(res, 200, { task_id: taskId, error: errorMessage(err) });