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,9 +1,10 @@
1
- import { existsSync, readFileSync } from "node:fs";
1
+ import { existsSync } from "node:fs";
2
2
  import { join, resolve } from "node:path";
3
- import { getConfig, getTasksDir } from "../config.js";
4
- import { guardReadPath } from "../security/pathGuard.js";
3
+ import { getConfig, getTasksDir } from "../../config.js";
4
+ import { guardReadPath } from "../../security/pathGuard.js";
5
5
  import { getTaskStatus } from "./getTaskStatus.js";
6
- import { redactSensitiveValue } from "../security/contentRedaction.js";
6
+ import { redactSensitiveValue } from "../../security/contentRedaction.js";
7
+ import { readTextFilePrefixSync, readTextFileTailLinesSync, } from "../../utils/boundedFile.js";
7
8
  const TERMINAL_STATUSES = new Set([
8
9
  "done",
9
10
  "done_by_agent",
@@ -30,7 +31,8 @@ export function getTaskSummary(taskId, options = {}) {
30
31
  ?? status.new_out_of_scope_changes
31
32
  ?? result.out_of_scope_changes
32
33
  ?? status.out_of_scope_changes);
33
- const verifyStatus = String(verify.status ?? result.verify_status ?? result.verify?.status ?? status.verify_status ?? "not_available");
34
+ const resultVerify = asRecord(result.verify);
35
+ const verifyStatus = String(verify.status ?? result.verify_status ?? resultVerify.status ?? status.verify_status ?? "not_available");
34
36
  const errors = [status.error, ...asArray(result.errors), ...asArray(result.known_issues)]
35
37
  .filter((value) => typeof value === "string" && value.trim() !== "");
36
38
  const warnings = asArray(result.warnings).filter((value) => typeof value === "string");
@@ -61,16 +63,16 @@ export function getTaskSummary(taskId, options = {}) {
61
63
  const acceptanceFile = join(taskDir, "acceptance.json");
62
64
  if (existsSync(acceptanceFile)) {
63
65
  try {
64
- const acceptance = JSON.parse(readFileSync(acceptanceFile, "utf-8"));
66
+ const acceptance = asRecord(JSON.parse(readTextFilePrefixSync(acceptanceFile, 1024 * 1024).content));
65
67
  if (acceptance.status === "accepted") {
66
68
  acceptanceStatus = "ready_for_review";
67
- acceptanceReviewedAt = acceptance.reviewed_at || null;
68
- acceptanceReviewer = acceptance.reviewer || null;
69
+ acceptanceReviewedAt = typeof acceptance.reviewed_at === "string" ? acceptance.reviewed_at : null;
70
+ acceptanceReviewer = typeof acceptance.reviewer === "string" ? acceptance.reviewer : null;
69
71
  }
70
72
  else if (acceptance.status === "rejected") {
71
73
  acceptanceStatus = "failed";
72
- acceptanceReviewedAt = acceptance.reviewed_at || null;
73
- acceptanceReviewer = acceptance.reviewer || null;
74
+ acceptanceReviewedAt = typeof acceptance.reviewed_at === "string" ? acceptance.reviewed_at : null;
75
+ acceptanceReviewer = typeof acceptance.reviewer === "string" ? acceptance.reviewer : null;
74
76
  if (acceptance.notes) {
75
77
  warnings.push(`Task was rejected by ${acceptance.reviewer || "human"} at ${acceptance.reviewed_at || "unknown time"}: ${acceptance.notes}`);
76
78
  }
@@ -102,11 +104,13 @@ export function getTaskSummary(taskId, options = {}) {
102
104
  const artifactHygiene = asRecord(result.artifact_hygiene ?? changedFilesRead.data.artifact_hygiene ?? {
103
105
  counts: status.artifact_hygiene_counts || {},
104
106
  });
105
- const verifyCommands = asArray(verify.commands ?? result.verify_commands ?? result.verify?.commands);
107
+ const verifyCommands = asArray(verify.commands ?? result.verify_commands ?? resultVerify.commands);
106
108
  const testLogSummary = summarizeTestLog(join(taskDir, "test.log"));
107
109
  const verificationSummary = buildVerificationSummary(verifyStatus, verifyCommands, testLogSummary);
108
110
  // Extract failed command detail from verify records
109
- const failedVerify = verifyCommands.find((cmd) => ["failed", "timed_out", "canceled"].includes(cmd?.status));
111
+ const failedVerify = verifyCommands
112
+ .map(asRecord)
113
+ .find((command) => ["failed", "timed_out", "canceled"].includes(String(command.status || "")));
110
114
  const failedCommandDetail = failedVerify ? {
111
115
  command: String(failedVerify.command || ""),
112
116
  exit_code: failedVerify.exit_code ?? null,
@@ -145,7 +149,7 @@ export function getTaskSummary(taskId, options = {}) {
145
149
  test_summary: verificationSummary.headline,
146
150
  diff_available: Boolean((status.diff_available ?? (changedFiles.length > 0)) &&
147
151
  (artifacts["diff.patch"] || artifacts["git.diff"])),
148
- diff_truncated: Boolean(status.diff_truncated || result.warnings?.some?.((warning) => warning.includes("diff.patch was truncated"))),
152
+ diff_truncated: Boolean(status.diff_truncated || asArray(result.warnings).some((warning) => typeof warning === "string" && warning.includes("diff.patch was truncated"))),
149
153
  result_available: artifacts["result.md"],
150
154
  result_json_available: artifacts["result.json"],
151
155
  verify_available: artifacts["verify.json"],
@@ -194,7 +198,9 @@ function buildCompactSummary(output, maxItems) {
194
198
  const manifestRead = tryReadJson(join(taskDir, "artifact_manifest.json"));
195
199
  const manifest = asRecord(manifestRead.data);
196
200
  const releaseArtifacts = asArray(manifest.artifacts);
197
- const releaseArtifactPaths = releaseArtifacts.map((a) => String(a.path || "")).slice(0, maxItems);
201
+ const releaseArtifactPaths = releaseArtifacts
202
+ .map((artifact) => String(asRecord(artifact).path || ""))
203
+ .slice(0, maxItems);
198
204
  const compact = {
199
205
  view: "compact",
200
206
  task_id: String(output.task_id),
@@ -243,7 +249,7 @@ function tryReadJson(path) {
243
249
  if (!existsSync(path))
244
250
  return { data: {} };
245
251
  try {
246
- return { data: JSON.parse(readFileSync(path, "utf-8")) };
252
+ return { data: asRecord(JSON.parse(readTextFilePrefixSync(path, 2 * 1024 * 1024).content)) };
247
253
  }
248
254
  catch (error) {
249
255
  return { data: {}, error: error instanceof Error ? error.message : String(error) };
@@ -253,27 +259,30 @@ function asArray(value) {
253
259
  return Array.isArray(value) ? value : [];
254
260
  }
255
261
  function asRecord(value) {
256
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
262
+ return value && typeof value === "object" && !Array.isArray(value)
263
+ ? value
264
+ : {};
257
265
  }
258
266
  function summarizeTestLog(path) {
259
267
  if (!existsSync(path))
260
268
  return "test.log missing";
261
- const text = readFileSync(path, "utf-8");
269
+ const text = readTextFileTailLinesSync(path, 200, 512 * 1024);
262
270
  const exit = text.match(/Exit\s*code:\s*([^\r\n]+)/i)?.[1]?.trim();
263
271
  return exit ? `Exit code: ${exit}` : text.trim().slice(0, 500) || "test.log empty";
264
272
  }
265
273
  function readResultFallback(path) {
266
274
  if (!existsSync(path))
267
275
  return "";
268
- const text = readFileSync(path, "utf-8");
276
+ const text = readTextFilePrefixSync(path, 1024 * 1024).content;
269
277
  return text.match(/## Summary\s+([\s\S]*?)(?:\n## |\n---|$)/i)?.[1]?.trim().slice(0, 1000) || "";
270
278
  }
271
279
  function buildVerificationSummary(status, commands, testLogSummary) {
272
- const passed = commands.filter((command) => command?.status === "passed").length;
273
- const failed = commands.filter((command) => ["failed", "timed_out", "canceled"].includes(command?.status)).length;
274
- const skipped = commands.filter((command) => command?.status === "skipped").length;
280
+ const commandRecords = commands.map(asRecord);
281
+ const passed = commandRecords.filter((command) => command.status === "passed").length;
282
+ const failed = commandRecords.filter((command) => ["failed", "timed_out", "canceled"].includes(String(command.status || ""))).length;
283
+ const skipped = commandRecords.filter((command) => command.status === "skipped").length;
275
284
  const evidenceText = [
276
- ...commands.flatMap((command) => [command?.stdout_tail, command?.stderr_tail]),
285
+ ...commandRecords.flatMap((command) => [command.stdout_tail, command.stderr_tail]),
277
286
  testLogSummary,
278
287
  ].filter((value) => typeof value === "string").join("\n");
279
288
  const headline = extractTestHeadline(evidenceText)
@@ -304,10 +313,8 @@ function readLogTail(path, lines) {
304
313
  if (!existsSync(path))
305
314
  return "(file not found)";
306
315
  try {
307
- const raw = readFileSync(path, "utf-8");
308
- if (raw.length === 0)
309
- return "(empty)";
310
- return raw.split("\n").slice(-lines).join("\n");
316
+ const tail = readTextFileTailLinesSync(path, lines);
317
+ return tail || "(empty)";
311
318
  }
312
319
  catch {
313
320
  return "(unreadable)";
@@ -0,0 +1,18 @@
1
+ export * from "./cancelTask.js";
2
+ export * from "./createTask.js";
3
+ export * from "./diagnoseTask.js";
4
+ export * from "./evidencePack.js";
5
+ export * from "./getTaskFile.js";
6
+ export * from "./getTaskProgress.js";
7
+ export * from "./getTaskStatus.js";
8
+ export * from "./getTaskStdoutTail.js";
9
+ export * from "./getTaskSummary.js";
10
+ export * from "./killTask.js";
11
+ export * from "./listTasks.js";
12
+ export * from "./reconcileTasks.js";
13
+ export * from "./retryTask.js";
14
+ export * from "./runTaskLoop.js";
15
+ export * from "./runVerification.js";
16
+ export * from "./taskLineage.js";
17
+ export * from "./taskOutputs.js";
18
+ export * from "./waitForTask.js";
@@ -0,0 +1,18 @@
1
+ export * from "./cancelTask.js";
2
+ export * from "./createTask.js";
3
+ export * from "./diagnoseTask.js";
4
+ export * from "./evidencePack.js";
5
+ export * from "./getTaskFile.js";
6
+ export * from "./getTaskProgress.js";
7
+ export * from "./getTaskStatus.js";
8
+ export * from "./getTaskStdoutTail.js";
9
+ export * from "./getTaskSummary.js";
10
+ export * from "./killTask.js";
11
+ export * from "./listTasks.js";
12
+ export * from "./reconcileTasks.js";
13
+ export * from "./retryTask.js";
14
+ export * from "./runTaskLoop.js";
15
+ export * from "./runVerification.js";
16
+ export * from "./taskLineage.js";
17
+ export * from "./taskOutputs.js";
18
+ export * from "./waitForTask.js";
@@ -0,0 +1 @@
1
+ export declare function killTask(taskId: string): import("./cancelTask.js").TaskTerminationResponse;
@@ -1,5 +1,5 @@
1
1
  import type { TaskPhase, TaskStatus, AcceptanceStatus } from "./createTask.js";
2
- import { type PendingReason, type WatcherState, type WatcherStatusSnapshot } from "../watcherStatus.js";
2
+ import { type PendingReason, type WatcherState, type WatcherStatusSnapshot } from "../../watcherStatus.js";
3
3
  export interface TaskEntry {
4
4
  task_id: string;
5
5
  plan_id: string;
@@ -1,8 +1,8 @@
1
1
  import { readdirSync, readFileSync, existsSync, statSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { getTasksDir, getPlansDir, getConfig } from "../config.js";
4
- import { readTaskRuntime } from "../taskRuntime.js";
5
- import { derivePendingReason, readWatcherStatus, } from "../watcherStatus.js";
3
+ import { getTasksDir, getPlansDir, getConfig } from "../../config.js";
4
+ import { readTaskRuntime } from "../../runner/taskRuntime.js";
5
+ import { derivePendingReason, readWatcherStatus, } from "../../watcherStatus.js";
6
6
  export function listTasks(input) {
7
7
  const config = getConfig();
8
8
  const tasksDir = getTasksDir(config);
@@ -1,4 +1,4 @@
1
- import { type PatchWardenConfig } from "../config.js";
1
+ import { type PatchWardenConfig } from "../../config.js";
2
2
  import { type DiagnosisType, type DiagnosisConfidence, type SafeAction } from "./diagnoseTask.js";
3
3
  export type ReconcileMode = "report_only" | "safe_fix";
4
4
  export interface ReconcileTasksInput {
@@ -1,8 +1,11 @@
1
- import { existsSync, readdirSync, readFileSync, writeFileSync, renameSync, unlinkSync, mkdirSync, statSync, } from "node:fs";
1
+ import { existsSync, readdirSync, readFileSync, mkdirSync, statSync, } from "node:fs";
2
2
  import { join, resolve, dirname } from "node:path";
3
- import { getTasksDir, getConfig } from "../config.js";
3
+ import { getTasksDir, getConfig } from "../../config.js";
4
4
  import { diagnoseTask } from "./diagnoseTask.js";
5
- import { syncSubgoalOnTaskDone, readTaskGoalMeta } from "../goal/subgoalSync.js";
5
+ import { syncSubgoalOnTaskDone, readTaskGoalMeta } from "../../goal/subgoalSync.js";
6
+ import { mutateTaskStatus } from "../../runner/taskStatusStore.js";
7
+ import { atomicWriteFileSync } from "../../utils/atomicFile.js";
8
+ import { appendBoundedTextFileSync } from "../../utils/boundedFile.js";
6
9
  // ── Defaults ────────────────────────────────────────────────────────
7
10
  const DEFAULT_MAX_AGE_MINUTES = 30;
8
11
  const RECONCILE_LOG_NAME = "reconcile.log";
@@ -137,10 +140,9 @@ export function reconcileTasks(input = {}, config = getConfig()) {
137
140
  reconciled += 1;
138
141
  }
139
142
  else {
140
- // Diagnosis type does not map to a safe_fix action — leave unchanged
141
143
  report.reasons = [
142
144
  ...report.reasons,
143
- `safe_fix skipped: diagnosis "${diagnosis.diagnosis}" has no automatic safe_fix action`,
145
+ `safe_fix skipped: ${fixResult.skip_reason || "task state could not be updated safely"}`,
144
146
  ];
145
147
  skippedLowConfidence += 1;
146
148
  }
@@ -208,77 +210,75 @@ function applySafeFix(taskDir, taskId, currentStatus, diagnosis, reasons, eviden
208
210
  previous_status: previousStatus,
209
211
  new_status: previousStatus,
210
212
  applied_at: new Date().toISOString(),
213
+ skip_reason: `diagnosis "${diagnosis}" has no automatic safe_fix action`,
211
214
  };
212
215
  }
213
216
  const appliedAt = new Date().toISOString();
214
217
  const statusFile = join(taskDir, "status.json");
215
218
  const backupFile = join(taskDir, "status.json.bak");
216
- // 1. Write status.json.bak with current contents (overwrite any prior backup)
219
+ // Revalidate and update under the shared task status lock. Any heartbeat or
220
+ // ownership-related status change after diagnosis makes this repair stale.
217
221
  try {
218
- const currentRaw = readFileSync(statusFile, "utf-8");
219
- writeFileSync(backupFile, currentRaw, "utf-8");
220
- }
221
- catch {
222
- // If we cannot back up, refuse to apply the fix.
223
- return {
224
- applied: false,
225
- action_taken: "left_unchanged",
226
- previous_status: previousStatus,
227
- new_status: previousStatus,
228
- applied_at: appliedAt,
229
- };
230
- }
231
- // 2. Build the new status record with full audit trail
232
- const next = {
233
- ...currentStatus,
234
- status: newStatus,
235
- phase: newStatus,
236
- updated_at: appliedAt,
237
- last_heartbeat_at: appliedAt,
238
- finished_at: appliedAt,
239
- // v0.7.0 audit fields
240
- previous_status: previousStatus,
241
- diagnosis: {
242
- type: diagnosis,
243
- confidence: "high",
244
- applied_by: "reconcile_tasks",
245
- applied_at: appliedAt,
246
- reasons,
247
- evidence: {
248
- heartbeat_age_seconds: evidence.heartbeat_age_seconds,
249
- stdout_age_seconds: evidence.stdout_age_seconds,
250
- child_pid: evidence.child_pid,
251
- child_pid_alive: evidence.child_pid_alive,
252
- watcher_instance_id: evidence.watcher_instance_id,
253
- current_watcher_instance_id: evidence.current_watcher_instance_id,
254
- },
255
- },
256
- };
257
- // For done_by_agent, set acceptance_status=pending (per roadmap 3.6)
258
- if (newStatus === "done_by_agent") {
259
- next.acceptance_status = "pending";
260
- next.legacy_status = "done";
261
- }
262
- // 3. Atomic write: write to temp file then rename
263
- const tmpFile = join(taskDir, "status.json.tmp");
264
- try {
265
- writeFileSync(tmpFile, JSON.stringify(next, null, 2), "utf-8");
266
- renameSync(tmpFile, statusFile);
267
- }
268
- catch {
269
- // Best-effort cleanup of temp file
270
- try {
271
- if (existsSync(tmpFile)) {
272
- unlinkSync(tmpFile);
222
+ const outcome = mutateTaskStatus(statusFile, (current) => {
223
+ if (JSON.stringify(current) !== JSON.stringify(currentStatus)) {
224
+ return { result: { applied: false, reason: "task status changed after diagnosis" } };
273
225
  }
226
+ const lockedPreviousStatus = typeof current.status === "string" ? current.status : "unknown";
227
+ const next = {
228
+ ...current,
229
+ status: newStatus,
230
+ phase: newStatus,
231
+ updated_at: appliedAt,
232
+ last_heartbeat_at: appliedAt,
233
+ finished_at: appliedAt,
234
+ previous_status: lockedPreviousStatus,
235
+ diagnosis: {
236
+ type: diagnosis,
237
+ confidence: "high",
238
+ applied_by: "reconcile_tasks",
239
+ applied_at: appliedAt,
240
+ reasons,
241
+ evidence: {
242
+ heartbeat_age_seconds: evidence.heartbeat_age_seconds,
243
+ stdout_age_seconds: evidence.stdout_age_seconds,
244
+ child_pid: evidence.child_pid,
245
+ child_pid_alive: evidence.child_pid_alive,
246
+ watcher_instance_id: evidence.watcher_instance_id,
247
+ current_watcher_instance_id: evidence.current_watcher_instance_id,
248
+ },
249
+ },
250
+ };
251
+ if (newStatus === "done_by_agent") {
252
+ next.acceptance_status = "pending";
253
+ next.legacy_status = "done";
254
+ }
255
+ // The backup is created while the same lock protects the exact record
256
+ // being replaced, so it cannot capture an unrelated newer state.
257
+ atomicWriteFileSync(backupFile, readFileSync(statusFile, "utf-8"));
258
+ return {
259
+ next,
260
+ result: { applied: true },
261
+ };
262
+ });
263
+ if (!outcome.applied) {
264
+ return {
265
+ applied: false,
266
+ action_taken: "left_unchanged",
267
+ previous_status: previousStatus,
268
+ new_status: previousStatus,
269
+ applied_at: appliedAt,
270
+ skip_reason: outcome.reason,
271
+ };
274
272
  }
275
- catch { /* ignore */ }
273
+ }
274
+ catch {
276
275
  return {
277
276
  applied: false,
278
277
  action_taken: "left_unchanged",
279
278
  previous_status: previousStatus,
280
279
  new_status: previousStatus,
281
280
  applied_at: appliedAt,
281
+ skip_reason: "task status could not be locked, backed up, and updated",
282
282
  };
283
283
  }
284
284
  // v0.8.0: 当状态变为 done_by_agent 时,同步关联 subgoal 状态(running → done_by_agent)
@@ -294,6 +294,7 @@ function applySafeFix(taskDir, taskId, currentStatus, diagnosis, reasons, eviden
294
294
  previous_status: previousStatus,
295
295
  new_status: newStatus,
296
296
  applied_at: appliedAt,
297
+ skip_reason: null,
297
298
  };
298
299
  }
299
300
  // ── reconcile.log writer ───────────────────────────────────────────
@@ -320,11 +321,8 @@ function writeReconcileLog(tasksDir, config, appliedReports) {
320
321
  evidence: report.evidence_summary,
321
322
  }));
322
323
  }
323
- // Append — do not overwrite history
324
- const existing = existsSync(logPath) ? readFileSync(logPath, "utf-8") : "";
325
- const nextContent = existing + (existing && !existing.endsWith("\n") ? "\n" : "") + lines.join("\n") + "\n";
326
324
  try {
327
- writeFileSync(logPath, nextContent, "utf-8");
325
+ appendBoundedTextFileSync(logPath, lines.join("\n") + "\n");
328
326
  }
329
327
  catch {
330
328
  // If we cannot write the log, the status change still happened —
@@ -0,0 +1,60 @@
1
+ import { readFileSync, existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { getTasksDir, getConfig } from "../../config.js";
4
+ import { guardReadPath } from "../../security/pathGuard.js";
5
+ import { createTask } from "./createTask.js";
6
+ export async function retryTask(taskId) {
7
+ const config = getConfig();
8
+ const tasksDir = getTasksDir(config);
9
+ const taskDir = join(tasksDir, taskId);
10
+ const statusFile = join(taskDir, "status.json");
11
+ guardReadPath(statusFile, config.workspaceRoot, config.tasksDir);
12
+ if (!existsSync(statusFile)) {
13
+ throw new Error(`Task not found: "${taskId}"`);
14
+ }
15
+ const parsed = JSON.parse(readFileSync(statusFile, "utf-8"));
16
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
17
+ throw new Error(`Task "${taskId}" has an invalid status record.`);
18
+ }
19
+ const data = parsed;
20
+ if (typeof data.plan_id !== "string" || !data.plan_id ||
21
+ typeof data.agent !== "string" || !data.agent) {
22
+ throw new Error(`Task "${taskId}" is missing plan_id or agent. Cannot retry.`);
23
+ }
24
+ const priorRetryCount = typeof data.retry_count === "number" &&
25
+ Number.isSafeInteger(data.retry_count) && data.retry_count >= 0
26
+ ? data.retry_count
27
+ : 0;
28
+ const planSource = data.plan_source === "inline" || data.plan_source === "template"
29
+ ? data.plan_source
30
+ : "saved";
31
+ const changePolicy = data.change_policy === "no_changes"
32
+ ? "no_changes"
33
+ : "repo_scoped_changes";
34
+ // Create a new task with the same parameters
35
+ const newTask = await createTask({
36
+ plan_id: data.plan_id,
37
+ agent: data.agent,
38
+ repo_path: typeof data.repo_path === "string" ? data.repo_path : undefined,
39
+ test_command: typeof data.test_command === "string" ? data.test_command : undefined,
40
+ verify_commands: Array.isArray(data.verify_commands)
41
+ ? data.verify_commands.filter((value) => typeof value === "string")
42
+ : undefined,
43
+ timeout_seconds: typeof data.timeout_seconds === "number" ? data.timeout_seconds : undefined,
44
+ retry_metadata: {
45
+ retry_of: taskId,
46
+ retry_count: priorRetryCount + 1,
47
+ plan_source: planSource,
48
+ template: typeof data.template === "string" ? data.template : null,
49
+ change_policy: changePolicy,
50
+ },
51
+ });
52
+ return {
53
+ original_task_id: taskId,
54
+ new_task_id: newTask.task_id,
55
+ plan_id: newTask.plan_id,
56
+ agent: newTask.agent,
57
+ status: newTask.status,
58
+ message: `New task created from retry of ${taskId}. Original task is unchanged.`,
59
+ };
60
+ }
@@ -1,11 +1,11 @@
1
- import { createWorktree } from "../goal/worktreeManager.js";
2
- import { createDirectSession } from "./createDirectSession.js";
1
+ import { createWorktree } from "../../goal/worktreeManager.js";
2
+ import { createDirectSession } from "../direct/createDirectSession.js";
3
3
  import { createTask } from "./createTask.js";
4
- import { recommendAgentForTask } from "./recommendAgentForTask.js";
5
- import { runDirectVerificationBundle } from "./runDirectVerificationBundle.js";
4
+ import { recommendAgentForTask } from "../workspace/recommendAgentForTask.js";
5
+ import { runDirectVerificationBundle } from "../direct/runDirectVerificationBundle.js";
6
6
  import { waitForTask } from "./waitForTask.js";
7
- import { safeAudit, safeAuditDirectSession, safeFinalizeDirectSession, safeResult, safeTestSummary } from "./safeViews.js";
8
- import type { TaskTemplateName } from "./taskTemplates.js";
7
+ import { safeAudit, safeAuditDirectSession, safeFinalizeDirectSession, safeResult, safeTestSummary } from "../diagnostics/safeViews.js";
8
+ import type { TaskTemplateName } from "../taskTemplates.js";
9
9
  import { createLineageId, writeTaskLineage, type SafeTaskLineage, type TaskLineageWorktree } from "./taskLineage.js";
10
10
  export interface RunTaskLoopInput {
11
11
  repo_path: string;