patchwarden 1.5.1 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (591) hide show
  1. package/PatchWarden.cmd +32 -2
  2. package/README.en.md +158 -26
  3. package/README.md +124 -24
  4. package/dist/assessments/agentAssessor.js +21 -12
  5. package/dist/assessments/assessmentDiagnostics.d.ts +29 -0
  6. package/dist/assessments/assessmentDiagnostics.js +132 -0
  7. package/dist/assessments/assessmentStore.d.ts +39 -0
  8. package/dist/assessments/assessmentStore.js +207 -45
  9. package/dist/assessments/securitySnapshot.d.ts +50 -0
  10. package/dist/assessments/securitySnapshot.js +158 -0
  11. package/dist/config.d.ts +15 -0
  12. package/dist/config.js +66 -13
  13. package/dist/control/fileManager.d.ts +2 -0
  14. package/dist/control/fileManager.js +39 -0
  15. package/dist/control/middleware/auth.d.ts +6 -0
  16. package/dist/control/middleware/auth.js +14 -5
  17. package/dist/control/middleware/static.js +16 -3
  18. package/dist/control/routeTable.d.ts +24 -0
  19. package/dist/control/routeTable.js +263 -0
  20. package/dist/control/routes/audit.js +17 -7
  21. package/dist/control/routes/evidence.js +1 -1
  22. package/dist/control/routes/lineage.js +1 -1
  23. package/dist/control/routes/process.d.ts +7 -0
  24. package/dist/control/routes/process.js +329 -42
  25. package/dist/control/routes/sessions.d.ts +9 -1
  26. package/dist/control/routes/sessions.js +84 -21
  27. package/dist/control/routes/status.d.ts +60 -1
  28. package/dist/control/routes/status.js +94 -15
  29. package/dist/control/routes/taskActions.js +24 -60
  30. package/dist/control/routes/tasks.d.ts +4 -0
  31. package/dist/control/routes/tasks.js +61 -62
  32. package/dist/control/routes/workspace.js +50 -23
  33. package/dist/control/runtime.d.ts +3 -2
  34. package/dist/control/runtime.js +65 -40
  35. package/dist/control/server.js +37 -365
  36. package/dist/control/shared.d.ts +25 -1
  37. package/dist/control/shared.js +64 -11
  38. package/dist/diagnostics/allowedTestCommandSafety.d.ts +1 -0
  39. package/dist/diagnostics/allowedTestCommandSafety.js +11 -0
  40. package/dist/direct/directAudit.js +234 -225
  41. package/dist/direct/directGuards.d.ts +2 -1
  42. package/dist/direct/directGuards.js +42 -29
  43. package/dist/direct/directPatch.d.ts +6 -1
  44. package/dist/direct/directPatch.js +55 -8
  45. package/dist/direct/directSessionStore.d.ts +16 -6
  46. package/dist/direct/directSessionStore.js +128 -35
  47. package/dist/direct/directVerification.js +8 -16
  48. package/dist/doctor.js +65 -57
  49. package/dist/goal/acceptanceEngine.d.ts +1 -1
  50. package/dist/goal/goalProgress.js +75 -69
  51. package/dist/goal/goalReport.js +5 -12
  52. package/dist/goal/goalStatus.d.ts +2 -0
  53. package/dist/goal/goalStatus.js +10 -3
  54. package/dist/goal/goalStore.d.ts +9 -2
  55. package/dist/goal/goalStore.js +152 -45
  56. package/dist/goal/handoffExport.js +6 -6
  57. package/dist/goal/specKitImport.js +97 -96
  58. package/dist/goal/subgoalSync.js +11 -9
  59. package/dist/goal/worktreeManager.d.ts +2 -0
  60. package/dist/goal/worktreeManager.js +209 -136
  61. package/dist/httpServer.js +111 -32
  62. package/dist/logging.js +5 -3
  63. package/dist/policy/projectPolicy.js +5 -2
  64. package/dist/release/releaseGate.d.ts +1 -1
  65. package/dist/release/releaseGate.js +78 -25
  66. package/dist/runner/agentInvocation.d.ts +14 -0
  67. package/dist/runner/agentInvocation.js +96 -4
  68. package/dist/runner/changeCapture.d.ts +13 -5
  69. package/dist/runner/changeCapture.js +243 -61
  70. package/dist/runner/postTaskCleanup.js +48 -19
  71. package/dist/runner/processSecurity.d.ts +56 -0
  72. package/dist/runner/processSecurity.js +373 -0
  73. package/dist/runner/runTask.d.ts +2 -1
  74. package/dist/runner/runTask.js +162 -93
  75. package/dist/runner/simpleProcess.d.ts +3 -0
  76. package/dist/runner/simpleProcess.js +50 -36
  77. package/dist/{taskProgress.d.ts → runner/taskProgress.d.ts} +1 -1
  78. package/dist/{taskProgress.js → runner/taskProgress.js} +2 -2
  79. package/dist/{taskRuntime.d.ts → runner/taskRuntime.d.ts} +3 -1
  80. package/dist/{taskRuntime.js → runner/taskRuntime.js} +3 -2
  81. package/dist/runner/taskStatusStore.d.ts +11 -0
  82. package/dist/runner/taskStatusStore.js +35 -0
  83. package/dist/runner/watch.d.ts +14 -1
  84. package/dist/runner/watch.js +320 -49
  85. package/dist/security/contentRedaction.js +2 -2
  86. package/dist/security/discoveryTokenStore.d.ts +3 -2
  87. package/dist/security/discoveryTokenStore.js +17 -2
  88. package/dist/security/loopbackHost.d.ts +2 -0
  89. package/dist/security/loopbackHost.js +16 -0
  90. package/dist/security/pathGuard.js +32 -17
  91. package/dist/security/planGuard.js +2 -1
  92. package/dist/security/runtimeGuard.js +9 -6
  93. package/dist/security/secretComparison.d.ts +3 -0
  94. package/dist/security/secretComparison.js +11 -0
  95. package/dist/security/sensitiveGuard.d.ts +5 -0
  96. package/dist/security/sensitiveGuard.js +27 -8
  97. package/dist/security/toolInvocationGuard.d.ts +2 -2
  98. package/dist/security/toolInvocationGuard.js +1 -1
  99. package/dist/security/workspaceRootGuard.d.ts +8 -0
  100. package/dist/security/workspaceRootGuard.js +47 -0
  101. package/dist/smoke-test.js +27 -26
  102. package/dist/tools/catalog/index.d.ts +4 -0
  103. package/dist/tools/catalog/index.js +4 -0
  104. package/dist/tools/{toolCatalog.js → catalog/toolCatalog.js} +3 -12
  105. package/dist/tools/{toolRegistry.d.ts → catalog/toolRegistry.d.ts} +3 -3
  106. package/dist/tools/{toolRegistry.js → catalog/toolRegistry.js} +6 -15
  107. package/dist/tools/definitions/toolDefs.d.ts +18 -0
  108. package/dist/tools/definitions/toolDefs.js +1177 -0
  109. package/dist/tools/{auditSession.d.ts → diagnostics/auditSession.d.ts} +1 -1
  110. package/dist/tools/{auditSession.js → diagnostics/auditSession.js} +2 -2
  111. package/dist/tools/{auditTask.d.ts → diagnostics/auditTask.d.ts} +2 -1
  112. package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +134 -36
  113. package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +18 -6
  114. package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +36 -8
  115. package/dist/tools/diagnostics/index.d.ts +6 -0
  116. package/dist/tools/diagnostics/index.js +6 -0
  117. package/dist/tools/{safeStatus.d.ts → diagnostics/safeStatus.d.ts} +3 -3
  118. package/dist/tools/{safeStatus.js → diagnostics/safeStatus.js} +6 -6
  119. package/dist/tools/{safeViews.d.ts → diagnostics/safeViews.d.ts} +12 -8
  120. package/dist/tools/{safeViews.js → diagnostics/safeViews.js} +23 -13
  121. package/dist/tools/{schemaDriftCheck.d.ts → diagnostics/schemaDriftCheck.d.ts} +1 -1
  122. package/dist/tools/{schemaDriftCheck.js → diagnostics/schemaDriftCheck.js} +2 -2
  123. package/dist/tools/{createDirectSession.js → direct/createDirectSession.js} +6 -6
  124. package/dist/tools/{finalizeDirectSession.d.ts → direct/finalizeDirectSession.d.ts} +1 -1
  125. package/dist/tools/direct/finalizeDirectSession.js +86 -0
  126. package/dist/tools/direct/index.d.ts +3 -0
  127. package/dist/tools/direct/index.js +3 -0
  128. package/dist/tools/{runDirectVerificationBundle.js → direct/runDirectVerificationBundle.js} +2 -2
  129. package/dist/tools/{discoverTools.d.ts → discovery/discoverTools.d.ts} +2 -2
  130. package/dist/tools/{discoverTools.js → discovery/discoverTools.js} +5 -5
  131. package/dist/tools/{explainTool.d.ts → discovery/explainTool.d.ts} +2 -2
  132. package/dist/tools/{explainTool.js → discovery/explainTool.js} +2 -2
  133. package/dist/tools/discovery/index.d.ts +3 -0
  134. package/dist/tools/discovery/index.js +3 -0
  135. package/dist/tools/{invokeDiscoveredTool.d.ts → discovery/invokeDiscoveredTool.d.ts} +2 -2
  136. package/dist/tools/{invokeDiscoveredTool.js → discovery/invokeDiscoveredTool.js} +5 -5
  137. package/dist/tools/dispatch/coreDispatch.js +32 -31
  138. package/dist/tools/dispatch/diagnosticDispatch.d.ts +3 -5
  139. package/dist/tools/dispatch/diagnosticDispatch.js +70 -68
  140. package/dist/tools/dispatch/directDispatch.js +11 -10
  141. package/dist/tools/dispatch/goalDispatch.js +5 -4
  142. package/dist/tools/dispatch/releaseDispatch.js +3 -2
  143. package/dist/tools/dispatch/validation.d.ts +7 -0
  144. package/dist/tools/dispatch/validation.js +71 -0
  145. package/dist/tools/{getPlan.js → goals/getPlan.js} +4 -4
  146. package/dist/tools/{goalSubgoalTask.d.ts → goals/goalSubgoalTask.d.ts} +1 -1
  147. package/dist/tools/goals/goalSubgoalTask.js +118 -0
  148. package/dist/tools/goals/index.d.ts +3 -0
  149. package/dist/tools/goals/index.js +3 -0
  150. package/dist/tools/{savePlan.js → goals/savePlan.js} +28 -8
  151. package/dist/tools/registry.d.ts +7 -11
  152. package/dist/tools/registry.js +17 -1178
  153. package/dist/tools/{checkReleaseGate.d.ts → release/checkReleaseGate.d.ts} +1 -1
  154. package/dist/tools/{checkReleaseGate.js → release/checkReleaseGate.js} +3 -3
  155. package/dist/tools/release/index.d.ts +2 -0
  156. package/dist/tools/release/index.js +2 -0
  157. package/dist/tools/{releaseMode.d.ts → release/releaseMode.d.ts} +2 -2
  158. package/dist/tools/{releaseMode.js → release/releaseMode.js} +46 -26
  159. package/dist/tools/tasks/cancelTask.d.ts +11 -0
  160. package/dist/tools/tasks/cancelTask.js +86 -0
  161. package/dist/tools/{createTask.d.ts → tasks/createTask.d.ts} +12 -4
  162. package/dist/tools/{createTask.js → tasks/createTask.js} +110 -34
  163. package/dist/tools/{diagnoseTask.d.ts → tasks/diagnoseTask.d.ts} +1 -1
  164. package/dist/tools/{diagnoseTask.js → tasks/diagnoseTask.js} +12 -16
  165. package/dist/tools/{evidencePack.js → tasks/evidencePack.js} +34 -22
  166. package/dist/tools/{getTaskFile.js → tasks/getTaskFile.js} +4 -4
  167. package/dist/tools/{getTaskStatus.d.ts → tasks/getTaskStatus.d.ts} +6 -1
  168. package/dist/tools/{getTaskStatus.js → tasks/getTaskStatus.js} +11 -6
  169. package/dist/tools/{getTaskStdoutTail.js → tasks/getTaskStdoutTail.js} +13 -11
  170. package/dist/tools/{getTaskSummary.js → tasks/getTaskSummary.js} +35 -36
  171. package/dist/tools/tasks/index.d.ts +18 -0
  172. package/dist/tools/tasks/index.js +18 -0
  173. package/dist/tools/tasks/killTask.d.ts +1 -0
  174. package/dist/tools/{listTasks.d.ts → tasks/listTasks.d.ts} +1 -1
  175. package/dist/tools/{listTasks.js → tasks/listTasks.js} +5 -4
  176. package/dist/tools/{reconcileTasks.d.ts → tasks/reconcileTasks.d.ts} +3 -2
  177. package/dist/tools/tasks/reconcileTasks.js +496 -0
  178. package/dist/tools/tasks/retryTask.js +60 -0
  179. package/dist/tools/{runTaskLoop.d.ts → tasks/runTaskLoop.d.ts} +6 -6
  180. package/dist/tools/{runTaskLoop.js → tasks/runTaskLoop.js} +63 -41
  181. package/dist/tools/tasks/runVerification.js +38 -0
  182. package/dist/tools/{taskLineage.js → tasks/taskLineage.js} +8 -7
  183. package/dist/tools/{taskOutputs.d.ts → tasks/taskOutputs.d.ts} +2 -2
  184. package/dist/tools/{taskOutputs.js → tasks/taskOutputs.js} +17 -16
  185. package/dist/tools/tasks/taskStates.d.ts +6 -0
  186. package/dist/tools/tasks/taskStates.js +52 -0
  187. package/dist/tools/{waitForTask.js → tasks/waitForTask.js} +3 -11
  188. package/dist/tools/{androidDoctor.js → workspace/androidDoctor.js} +16 -10
  189. package/dist/tools/{applyPatch.d.ts → workspace/applyPatch.d.ts} +1 -1
  190. package/dist/tools/workspace/applyPatch.js +53 -0
  191. package/dist/tools/{discardWorktree.js → workspace/discardWorktree.js} +3 -3
  192. package/dist/tools/workspace/index.d.ts +10 -0
  193. package/dist/tools/workspace/index.js +10 -0
  194. package/dist/tools/workspace/listAgents.d.ts +23 -0
  195. package/dist/tools/workspace/listAgents.js +56 -0
  196. package/dist/tools/{listWorkspace.js → workspace/listWorkspace.js} +3 -3
  197. package/dist/tools/{mergeWorktree.js → workspace/mergeWorktree.js} +3 -3
  198. package/dist/tools/{readWorkspaceFile.js → workspace/readWorkspaceFile.js} +8 -8
  199. package/dist/tools/{recommendAgentForTask.js → workspace/recommendAgentForTask.js} +3 -3
  200. package/dist/tools/{searchWorkspace.js → workspace/searchWorkspace.js} +5 -5
  201. package/dist/tools/{syncFile.d.ts → workspace/syncFile.d.ts} +1 -1
  202. package/dist/tools/workspace/syncFile.js +134 -0
  203. package/dist/utils/atomicFile.d.ts +7 -0
  204. package/dist/utils/atomicFile.js +67 -0
  205. package/dist/utils/boundedFile.d.ts +8 -0
  206. package/dist/utils/boundedFile.js +93 -0
  207. package/dist/utils/lockedJsonFile.d.ts +15 -0
  208. package/dist/utils/lockedJsonFile.js +224 -0
  209. package/dist/utils/platform.d.ts +11 -0
  210. package/dist/utils/platform.js +26 -0
  211. package/dist/utils/stableJson.d.ts +2 -0
  212. package/dist/utils/stableJson.js +12 -0
  213. package/dist/version.d.ts +3 -2
  214. package/dist/version.js +3 -2
  215. package/dist/watcherStatus.d.ts +2 -2
  216. package/dist/watcherStatus.js +18 -0
  217. package/docs/CODE_WIKI.md +808 -143
  218. package/docs/architecture.md +15 -55
  219. package/docs/assets/patchwarden-desktop-onboarding.png +0 -0
  220. package/docs/control-center/README.md +4 -4
  221. package/docs/dashboard-overview.md +4 -4
  222. package/docs/desktop-app.md +175 -0
  223. package/docs/evidence-pack-schema.md +3 -3
  224. package/docs/open-source-application.md +16 -11
  225. package/docs/release-checklist.md +1 -1
  226. package/docs/release-evidence.md +35 -39
  227. package/docs/task-safe-review-workflow.md +5 -5
  228. package/docs/threat-model.md +19 -1
  229. package/examples/config.example.json +4 -2
  230. package/package.json +27 -10
  231. package/scripts/README.md +15 -6
  232. package/scripts/build.js +11 -0
  233. package/scripts/checks/brand-check.js +3 -0
  234. package/scripts/checks/build-output-check.js +37 -0
  235. package/scripts/checks/control-center-smoke.js +102 -10
  236. package/scripts/checks/control-smoke.js +19 -8
  237. package/scripts/checks/http-mcp-smoke.js +82 -4
  238. package/scripts/checks/lifecycle-smoke.js +15 -12
  239. package/scripts/checks/mcp-manifest-check.js +1 -1
  240. package/scripts/checks/mcp-smoke.js +58 -4
  241. package/scripts/checks/package-manifest-check.js +20 -7
  242. package/scripts/checks/unit-tests.js +3 -5
  243. package/scripts/checks/watcher-supervisor-smoke.js +38 -1
  244. package/scripts/control/get-patchwarden-health.ps1 +1 -1
  245. package/scripts/control/manage-patchwarden.ps1 +69 -4
  246. package/scripts/control/provision-patchwarden-tunnel.ps1 +127 -0
  247. package/scripts/control/run-background-supervisor.ps1 +41 -0
  248. package/scripts/control/start-patchwarden-tunnel.ps1 +191 -33
  249. package/scripts/e2e/demo-runtime.mjs +153 -0
  250. package/{PatchWarden-Control-Tray.cmd → scripts/launchers/PatchWarden-Control-Tray.cmd} +3 -3
  251. package/scripts/launchers/PatchWarden-Control.cmd +6 -0
  252. package/{PatchWarden-Desktop.cmd → scripts/launchers/PatchWarden-Desktop.cmd} +2 -2
  253. package/scripts/launchers/Restart-PatchWarden-Control.cmd +6 -0
  254. package/{Stop-PatchWarden.cmd → scripts/launchers/Stop-PatchWarden.cmd} +2 -2
  255. package/scripts/lib/clean-generated-output.js +11 -0
  256. package/scripts/lib/file-discovery.js +12 -0
  257. package/scripts/release/desktop-preflight.js +211 -0
  258. package/scripts/release/pack-clean.js +59 -14
  259. package/src/assessments/agentAssessor.ts +26 -12
  260. package/src/assessments/assessmentDiagnostics.ts +172 -0
  261. package/src/assessments/assessmentStore.ts +288 -51
  262. package/src/assessments/securitySnapshot.ts +231 -0
  263. package/src/config.ts +70 -13
  264. package/src/control/fileManager.ts +40 -0
  265. package/src/control/middleware/auth.ts +22 -4
  266. package/src/control/middleware/static.ts +16 -3
  267. package/src/control/routeTable.ts +301 -0
  268. package/src/control/routes/audit.ts +14 -6
  269. package/src/control/routes/evidence.ts +1 -1
  270. package/src/control/routes/lineage.ts +1 -1
  271. package/src/control/routes/process.ts +341 -40
  272. package/src/control/routes/sessions.ts +88 -25
  273. package/src/control/routes/status.ts +134 -17
  274. package/src/control/routes/taskActions.ts +24 -58
  275. package/src/control/routes/tasks.ts +66 -58
  276. package/src/control/routes/workspace.ts +56 -25
  277. package/src/control/runtime.ts +71 -43
  278. package/src/control/server.ts +37 -342
  279. package/src/control/shared.ts +77 -11
  280. package/src/diagnostics/allowedTestCommandSafety.ts +12 -0
  281. package/src/direct/directAudit.ts +17 -9
  282. package/src/direct/directGuards.ts +72 -44
  283. package/src/direct/directPatch.ts +95 -8
  284. package/src/direct/directSessionStore.ts +213 -55
  285. package/src/direct/directVerification.ts +9 -23
  286. package/src/doctor.ts +76 -64
  287. package/src/goal/acceptanceEngine.ts +1 -1
  288. package/src/goal/goalProgress.ts +17 -13
  289. package/src/goal/goalReport.ts +5 -13
  290. package/src/goal/goalStatus.ts +19 -3
  291. package/src/goal/goalStore.ts +241 -54
  292. package/src/goal/handoffExport.ts +6 -6
  293. package/src/goal/specKitImport.ts +12 -12
  294. package/src/goal/subgoalSync.ts +11 -8
  295. package/src/goal/worktreeManager.ts +139 -22
  296. package/src/httpServer.ts +113 -30
  297. package/src/logging.ts +10 -3
  298. package/src/policy/projectPolicy.ts +5 -2
  299. package/src/release/releaseGate.ts +74 -27
  300. package/src/runner/agentInvocation.ts +126 -4
  301. package/src/runner/changeCapture.ts +291 -57
  302. package/src/runner/postTaskCleanup.ts +41 -17
  303. package/src/runner/processSecurity.ts +433 -0
  304. package/src/runner/runTask.ts +197 -97
  305. package/src/runner/simpleProcess.ts +66 -31
  306. package/src/{taskProgress.ts → runner/taskProgress.ts} +3 -3
  307. package/src/{taskRuntime.ts → runner/taskRuntime.ts} +6 -3
  308. package/src/runner/taskStatusStore.ts +69 -0
  309. package/src/runner/watch.ts +348 -52
  310. package/src/security/contentRedaction.ts +2 -2
  311. package/src/security/discoveryTokenStore.ts +18 -4
  312. package/src/security/loopbackHost.ts +19 -0
  313. package/src/security/pathGuard.ts +35 -16
  314. package/src/security/planGuard.ts +2 -1
  315. package/src/security/runtimeGuard.ts +9 -6
  316. package/src/security/secretComparison.ts +13 -0
  317. package/src/security/sensitiveGuard.ts +27 -8
  318. package/src/security/toolInvocationGuard.ts +2 -2
  319. package/src/security/workspaceRootGuard.ts +53 -0
  320. package/src/smoke-test.ts +27 -26
  321. package/src/tools/catalog/index.ts +4 -0
  322. package/src/tools/{toolCatalog.ts → catalog/toolCatalog.ts} +3 -12
  323. package/src/tools/{toolRegistry.ts → catalog/toolRegistry.ts} +7 -16
  324. package/src/tools/definitions/toolDefs.ts +1264 -0
  325. package/src/tools/{auditSession.ts → diagnostics/auditSession.ts} +2 -2
  326. package/src/tools/{auditTask.ts → diagnostics/auditTask.ts} +136 -39
  327. package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +37 -9
  328. package/src/tools/diagnostics/index.ts +6 -0
  329. package/src/tools/{safeStatus.ts → diagnostics/safeStatus.ts} +7 -7
  330. package/src/tools/{safeViews.ts → diagnostics/safeViews.ts} +27 -17
  331. package/src/tools/{schemaDriftCheck.ts → diagnostics/schemaDriftCheck.ts} +3 -3
  332. package/src/tools/{createDirectSession.ts → direct/createDirectSession.ts} +6 -6
  333. package/src/tools/{finalizeDirectSession.ts → direct/finalizeDirectSession.ts} +7 -5
  334. package/src/tools/direct/index.ts +3 -0
  335. package/src/tools/{runDirectVerificationBundle.ts → direct/runDirectVerificationBundle.ts} +2 -2
  336. package/src/tools/{discoverTools.ts → discovery/discoverTools.ts} +6 -6
  337. package/src/tools/{explainTool.ts → discovery/explainTool.ts} +3 -3
  338. package/src/tools/discovery/index.ts +3 -0
  339. package/src/tools/{invokeDiscoveredTool.ts → discovery/invokeDiscoveredTool.ts} +7 -7
  340. package/src/tools/dispatch/coreDispatch.ts +36 -31
  341. package/src/tools/dispatch/diagnosticDispatch.ts +26 -21
  342. package/src/tools/dispatch/directDispatch.ts +11 -10
  343. package/src/tools/dispatch/goalDispatch.ts +5 -4
  344. package/src/tools/dispatch/releaseDispatch.ts +3 -2
  345. package/src/tools/dispatch/validation.ts +78 -0
  346. package/src/tools/{getPlan.ts → goals/getPlan.ts} +4 -4
  347. package/src/tools/{goalSubgoalTask.ts → goals/goalSubgoalTask.ts} +41 -26
  348. package/src/tools/goals/index.ts +3 -0
  349. package/src/tools/{savePlan.ts → goals/savePlan.ts} +38 -8
  350. package/src/tools/registry.ts +17 -1261
  351. package/src/tools/{checkReleaseGate.ts → release/checkReleaseGate.ts} +3 -3
  352. package/src/tools/release/index.ts +2 -0
  353. package/src/tools/{releaseMode.ts → release/releaseMode.ts} +42 -24
  354. package/src/tools/tasks/cancelTask.ts +110 -0
  355. package/src/tools/{createTask.ts → tasks/createTask.ts} +161 -33
  356. package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +12 -16
  357. package/src/tools/{evidencePack.ts → tasks/evidencePack.ts} +36 -22
  358. package/src/tools/{getTaskFile.ts → tasks/getTaskFile.ts} +4 -4
  359. package/src/tools/{getTaskStatus.ts → tasks/getTaskStatus.ts} +13 -6
  360. package/src/tools/{getTaskStdoutTail.ts → tasks/getTaskStdoutTail.ts} +13 -11
  361. package/src/tools/{getTaskSummary.ts → tasks/getTaskSummary.ts} +46 -44
  362. package/src/tools/tasks/index.ts +18 -0
  363. package/src/tools/{listTasks.ts → tasks/listTasks.ts} +6 -5
  364. package/src/tools/{reconcileTasks.ts → tasks/reconcileTasks.ts} +228 -77
  365. package/src/tools/tasks/retryTask.ts +69 -0
  366. package/src/tools/{runTaskLoop.ts → tasks/runTaskLoop.ts} +73 -46
  367. package/src/tools/{runVerification.ts → tasks/runVerification.ts} +6 -3
  368. package/src/tools/{taskLineage.ts → tasks/taskLineage.ts} +8 -7
  369. package/src/tools/{taskOutputs.ts → tasks/taskOutputs.ts} +27 -20
  370. package/src/tools/tasks/taskStates.ts +54 -0
  371. package/src/tools/{waitForTask.ts → tasks/waitForTask.ts} +3 -12
  372. package/src/tools/{androidDoctor.ts → workspace/androidDoctor.ts} +15 -10
  373. package/src/tools/{applyPatch.ts → workspace/applyPatch.ts} +38 -16
  374. package/src/tools/{discardWorktree.ts → workspace/discardWorktree.ts} +3 -3
  375. package/src/tools/workspace/index.ts +10 -0
  376. package/src/tools/workspace/listAgents.ts +75 -0
  377. package/src/tools/{listWorkspace.ts → workspace/listWorkspace.ts} +3 -3
  378. package/src/tools/{mergeWorktree.ts → workspace/mergeWorktree.ts} +3 -3
  379. package/src/tools/{readWorkspaceFile.ts → workspace/readWorkspaceFile.ts} +8 -8
  380. package/src/tools/{recommendAgentForTask.ts → workspace/recommendAgentForTask.ts} +3 -3
  381. package/src/tools/{searchWorkspace.ts → workspace/searchWorkspace.ts} +5 -5
  382. package/src/tools/workspace/syncFile.ts +292 -0
  383. package/src/utils/atomicFile.ts +74 -0
  384. package/src/utils/boundedFile.ts +116 -0
  385. package/src/utils/lockedJsonFile.ts +251 -0
  386. package/src/utils/platform.ts +31 -0
  387. package/src/utils/stableJson.ts +12 -0
  388. package/src/version.ts +3 -2
  389. package/src/watcherStatus.ts +19 -1
  390. package/ui/desktop-bootstrap.js +11 -0
  391. package/ui/desktop-bridge.js +36 -0
  392. package/ui/desktop.css +160 -0
  393. package/ui/getting-started.js +93 -0
  394. package/ui/i18n.js +484 -0
  395. package/ui/log-parser.js +30 -0
  396. package/ui/pages/audit.html +12 -0
  397. package/ui/pages/dashboard.html +94 -147
  398. package/ui/pages/direct-sessions.html +123 -26
  399. package/ui/pages/getting-started.html +43 -0
  400. package/ui/pages/logs.html +134 -22
  401. package/ui/pages/settings.html +101 -0
  402. package/ui/pages/task-detail.html +12 -0
  403. package/ui/pages/tasks.html +54 -8
  404. package/ui/pages/workspace.html +38 -1
  405. package/ui/settings.js +254 -0
  406. package/PatchWarden-Control.cmd +0 -6
  407. package/Restart-PatchWarden-Control.cmd +0 -6
  408. package/dist/test/unit/acceptance-engine.test.d.ts +0 -1
  409. package/dist/test/unit/acceptance-engine.test.js +0 -228
  410. package/dist/test/unit/agent-router.test.d.ts +0 -1
  411. package/dist/test/unit/agent-router.test.js +0 -287
  412. package/dist/test/unit/android-doctor.test.d.ts +0 -1
  413. package/dist/test/unit/android-doctor.test.js +0 -118
  414. package/dist/test/unit/apply-patch.test.d.ts +0 -1
  415. package/dist/test/unit/apply-patch.test.js +0 -225
  416. package/dist/test/unit/audit-checks.test.d.ts +0 -1
  417. package/dist/test/unit/audit-checks.test.js +0 -350
  418. package/dist/test/unit/chinese-path.test.d.ts +0 -1
  419. package/dist/test/unit/chinese-path.test.js +0 -91
  420. package/dist/test/unit/command-guard.test.d.ts +0 -1
  421. package/dist/test/unit/command-guard.test.js +0 -160
  422. package/dist/test/unit/create-task.test.d.ts +0 -1
  423. package/dist/test/unit/create-task.test.js +0 -197
  424. package/dist/test/unit/diagnose-task.test.d.ts +0 -1
  425. package/dist/test/unit/diagnose-task.test.js +0 -457
  426. package/dist/test/unit/direct-guards.test.d.ts +0 -1
  427. package/dist/test/unit/direct-guards.test.js +0 -328
  428. package/dist/test/unit/discovery-token-store.test.d.ts +0 -1
  429. package/dist/test/unit/discovery-token-store.test.js +0 -139
  430. package/dist/test/unit/evidence-pack.test.d.ts +0 -1
  431. package/dist/test/unit/evidence-pack.test.js +0 -224
  432. package/dist/test/unit/get-task-status.test.d.ts +0 -1
  433. package/dist/test/unit/get-task-status.test.js +0 -174
  434. package/dist/test/unit/get-task-summary.test.d.ts +0 -1
  435. package/dist/test/unit/get-task-summary.test.js +0 -146
  436. package/dist/test/unit/goal-graph.test.d.ts +0 -1
  437. package/dist/test/unit/goal-graph.test.js +0 -298
  438. package/dist/test/unit/goal-progress.test.d.ts +0 -1
  439. package/dist/test/unit/goal-progress.test.js +0 -381
  440. package/dist/test/unit/goal-report.test.d.ts +0 -1
  441. package/dist/test/unit/goal-report.test.js +0 -159
  442. package/dist/test/unit/goal-status.test.d.ts +0 -1
  443. package/dist/test/unit/goal-status.test.js +0 -215
  444. package/dist/test/unit/goal-store.test.d.ts +0 -1
  445. package/dist/test/unit/goal-store.test.js +0 -253
  446. package/dist/test/unit/goal-subgoal-task.test.d.ts +0 -1
  447. package/dist/test/unit/goal-subgoal-task.test.js +0 -55
  448. package/dist/test/unit/goal-tools-registry.test.d.ts +0 -1
  449. package/dist/test/unit/goal-tools-registry.test.js +0 -192
  450. package/dist/test/unit/handoff-export.test.d.ts +0 -1
  451. package/dist/test/unit/handoff-export.test.js +0 -263
  452. package/dist/test/unit/invoke-discovered-tool.test.d.ts +0 -1
  453. package/dist/test/unit/invoke-discovered-tool.test.js +0 -167
  454. package/dist/test/unit/logging.test.d.ts +0 -1
  455. package/dist/test/unit/logging.test.js +0 -397
  456. package/dist/test/unit/path-guard.test.d.ts +0 -1
  457. package/dist/test/unit/path-guard.test.js +0 -133
  458. package/dist/test/unit/post-task-cleanup.test.d.ts +0 -1
  459. package/dist/test/unit/post-task-cleanup.test.js +0 -48
  460. package/dist/test/unit/project-policy-release-mode.test.d.ts +0 -1
  461. package/dist/test/unit/project-policy-release-mode.test.js +0 -125
  462. package/dist/test/unit/reconcile-tasks.test.d.ts +0 -1
  463. package/dist/test/unit/reconcile-tasks.test.js +0 -456
  464. package/dist/test/unit/release-gate.test.d.ts +0 -1
  465. package/dist/test/unit/release-gate.test.js +0 -242
  466. package/dist/test/unit/run-task-loop.test.d.ts +0 -1
  467. package/dist/test/unit/run-task-loop.test.js +0 -380
  468. package/dist/test/unit/safe-status.test.d.ts +0 -1
  469. package/dist/test/unit/safe-status.test.js +0 -165
  470. package/dist/test/unit/safe-views.test.d.ts +0 -1
  471. package/dist/test/unit/safe-views.test.js +0 -171
  472. package/dist/test/unit/schema-drift-check.test.d.ts +0 -1
  473. package/dist/test/unit/schema-drift-check.test.js +0 -176
  474. package/dist/test/unit/sensitive-guard.test.d.ts +0 -1
  475. package/dist/test/unit/sensitive-guard.test.js +0 -104
  476. package/dist/test/unit/spec-kit-import.test.d.ts +0 -1
  477. package/dist/test/unit/spec-kit-import.test.js +0 -341
  478. package/dist/test/unit/subgoal-sync.test.d.ts +0 -1
  479. package/dist/test/unit/subgoal-sync.test.js +0 -183
  480. package/dist/test/unit/sync-file.test.d.ts +0 -1
  481. package/dist/test/unit/sync-file.test.js +0 -154
  482. package/dist/test/unit/tool-invocation-guard.test.d.ts +0 -1
  483. package/dist/test/unit/tool-invocation-guard.test.js +0 -432
  484. package/dist/test/unit/tool-usage-stats.test.d.ts +0 -1
  485. package/dist/test/unit/tool-usage-stats.test.js +0 -300
  486. package/dist/test/unit/toolSearch.test.d.ts +0 -1
  487. package/dist/test/unit/toolSearch.test.js +0 -571
  488. package/dist/test/unit/wait-for-task.test.d.ts +0 -1
  489. package/dist/test/unit/wait-for-task.test.js +0 -144
  490. package/dist/test/unit/watcher-status.test.d.ts +0 -1
  491. package/dist/test/unit/watcher-status.test.js +0 -169
  492. package/dist/test/unit/worktree-manager.test.d.ts +0 -1
  493. package/dist/test/unit/worktree-manager.test.js +0 -176
  494. package/dist/tools/applyPatch.js +0 -41
  495. package/dist/tools/cancelTask.d.ts +0 -30
  496. package/dist/tools/cancelTask.js +0 -64
  497. package/dist/tools/finalizeDirectSession.js +0 -84
  498. package/dist/tools/goalSubgoalTask.js +0 -110
  499. package/dist/tools/killTask.d.ts +0 -15
  500. package/dist/tools/listAgents.d.ts +0 -12
  501. package/dist/tools/listAgents.js +0 -42
  502. package/dist/tools/reconcileTasks.js +0 -352
  503. package/dist/tools/retryTask.js +0 -45
  504. package/dist/tools/runVerification.js +0 -36
  505. package/dist/tools/syncFile.js +0 -65
  506. package/docs/control-center/control-center-daily-driver.md +0 -211
  507. package/docs/control-center/control-center-mvp.md +0 -205
  508. package/docs/control-center/control-center-phase2.md +0 -159
  509. package/docs/execution-plan-2026-07-09.md +0 -315
  510. package/docs/release-v0.6.0.md +0 -71
  511. package/docs/release-v0.6.1.md +0 -75
  512. package/docs/release-v0.6.4.md +0 -45
  513. package/docs/roadmap-execution-and-acceptance.md +0 -365
  514. package/src/test/unit/acceptance-engine.test.ts +0 -261
  515. package/src/test/unit/agent-router.test.ts +0 -342
  516. package/src/test/unit/android-doctor.test.ts +0 -158
  517. package/src/test/unit/apply-patch.test.ts +0 -293
  518. package/src/test/unit/audit-checks.test.ts +0 -567
  519. package/src/test/unit/chinese-path.test.ts +0 -106
  520. package/src/test/unit/command-guard.test.ts +0 -221
  521. package/src/test/unit/create-task.test.ts +0 -255
  522. package/src/test/unit/diagnose-task.test.ts +0 -544
  523. package/src/test/unit/direct-guards.test.ts +0 -467
  524. package/src/test/unit/discovery-token-store.test.ts +0 -181
  525. package/src/test/unit/evidence-pack.test.ts +0 -251
  526. package/src/test/unit/get-task-status.test.ts +0 -203
  527. package/src/test/unit/get-task-summary.test.ts +0 -173
  528. package/src/test/unit/goal-graph.test.ts +0 -347
  529. package/src/test/unit/goal-progress.test.ts +0 -538
  530. package/src/test/unit/goal-report.test.ts +0 -189
  531. package/src/test/unit/goal-status.test.ts +0 -270
  532. package/src/test/unit/goal-store.test.ts +0 -318
  533. package/src/test/unit/goal-subgoal-task.test.ts +0 -72
  534. package/src/test/unit/goal-tools-registry.test.ts +0 -245
  535. package/src/test/unit/handoff-export.test.ts +0 -295
  536. package/src/test/unit/invoke-discovered-tool.test.ts +0 -216
  537. package/src/test/unit/logging.test.ts +0 -497
  538. package/src/test/unit/path-guard.test.ts +0 -180
  539. package/src/test/unit/post-task-cleanup.test.ts +0 -53
  540. package/src/test/unit/project-policy-release-mode.test.ts +0 -156
  541. package/src/test/unit/reconcile-tasks.test.ts +0 -551
  542. package/src/test/unit/release-gate.test.ts +0 -314
  543. package/src/test/unit/run-task-loop.test.ts +0 -425
  544. package/src/test/unit/safe-status.test.ts +0 -187
  545. package/src/test/unit/safe-views.test.ts +0 -184
  546. package/src/test/unit/schema-drift-check.test.ts +0 -260
  547. package/src/test/unit/sensitive-guard.test.ts +0 -124
  548. package/src/test/unit/spec-kit-import.test.ts +0 -429
  549. package/src/test/unit/subgoal-sync.test.ts +0 -236
  550. package/src/test/unit/sync-file.test.ts +0 -231
  551. package/src/test/unit/tool-invocation-guard.test.ts +0 -542
  552. package/src/test/unit/tool-usage-stats.test.ts +0 -384
  553. package/src/test/unit/toolSearch.test.ts +0 -756
  554. package/src/test/unit/wait-for-task.test.ts +0 -176
  555. package/src/test/unit/watcher-status.test.ts +0 -190
  556. package/src/test/unit/worktree-manager.test.ts +0 -247
  557. package/src/tools/cancelTask.ts +0 -70
  558. package/src/tools/listAgents.ts +0 -54
  559. package/src/tools/retryTask.ts +0 -53
  560. package/src/tools/syncFile.ts +0 -122
  561. /package/dist/tools/{toolCatalog.d.ts → catalog/toolCatalog.d.ts} +0 -0
  562. /package/dist/tools/{toolSearch.d.ts → catalog/toolSearch.d.ts} +0 -0
  563. /package/dist/tools/{toolSearch.js → catalog/toolSearch.js} +0 -0
  564. /package/dist/tools/{toolUsageStats.d.ts → catalog/toolUsageStats.d.ts} +0 -0
  565. /package/dist/tools/{toolUsageStats.js → catalog/toolUsageStats.js} +0 -0
  566. /package/dist/tools/{createDirectSession.d.ts → direct/createDirectSession.d.ts} +0 -0
  567. /package/dist/tools/{runDirectVerificationBundle.d.ts → direct/runDirectVerificationBundle.d.ts} +0 -0
  568. /package/dist/tools/{getPlan.d.ts → goals/getPlan.d.ts} +0 -0
  569. /package/dist/tools/{savePlan.d.ts → goals/savePlan.d.ts} +0 -0
  570. /package/dist/tools/{evidencePack.d.ts → tasks/evidencePack.d.ts} +0 -0
  571. /package/dist/tools/{getTaskFile.d.ts → tasks/getTaskFile.d.ts} +0 -0
  572. /package/dist/tools/{getTaskProgress.d.ts → tasks/getTaskProgress.d.ts} +0 -0
  573. /package/dist/tools/{getTaskProgress.js → tasks/getTaskProgress.js} +0 -0
  574. /package/dist/tools/{getTaskStdoutTail.d.ts → tasks/getTaskStdoutTail.d.ts} +0 -0
  575. /package/dist/tools/{getTaskSummary.d.ts → tasks/getTaskSummary.d.ts} +0 -0
  576. /package/dist/tools/{killTask.js → tasks/killTask.js} +0 -0
  577. /package/dist/tools/{retryTask.d.ts → tasks/retryTask.d.ts} +0 -0
  578. /package/dist/tools/{runVerification.d.ts → tasks/runVerification.d.ts} +0 -0
  579. /package/dist/tools/{taskLineage.d.ts → tasks/taskLineage.d.ts} +0 -0
  580. /package/dist/tools/{waitForTask.d.ts → tasks/waitForTask.d.ts} +0 -0
  581. /package/dist/tools/{androidDoctor.d.ts → workspace/androidDoctor.d.ts} +0 -0
  582. /package/dist/tools/{discardWorktree.d.ts → workspace/discardWorktree.d.ts} +0 -0
  583. /package/dist/tools/{listWorkspace.d.ts → workspace/listWorkspace.d.ts} +0 -0
  584. /package/dist/tools/{mergeWorktree.d.ts → workspace/mergeWorktree.d.ts} +0 -0
  585. /package/dist/tools/{readWorkspaceFile.d.ts → workspace/readWorkspaceFile.d.ts} +0 -0
  586. /package/dist/tools/{recommendAgentForTask.d.ts → workspace/recommendAgentForTask.d.ts} +0 -0
  587. /package/dist/tools/{searchWorkspace.d.ts → workspace/searchWorkspace.d.ts} +0 -0
  588. /package/src/tools/{toolSearch.ts → catalog/toolSearch.ts} +0 -0
  589. /package/src/tools/{toolUsageStats.ts → catalog/toolUsageStats.ts} +0 -0
  590. /package/src/tools/{getTaskProgress.ts → tasks/getTaskProgress.ts} +0 -0
  591. /package/src/tools/{killTask.ts → tasks/killTask.ts} +0 -0
@@ -1,84 +0,0 @@
1
- import { join, resolve, relative, isAbsolute } from "node:path";
2
- import { readDirectSession, finalizeDirectSessionRecord, getDirectSessionDir, } from "../direct/directSessionStore.js";
3
- import { guardDirectSessionActive } from "../direct/directGuards.js";
4
- import { captureRepoSnapshot, buildChangeArtifacts, } from "../runner/changeCapture.js";
5
- // ── Main function ──────────────────────────────────────────────────
6
- export async function finalizeDirectSession(input) {
7
- const { session_id } = input;
8
- // 1. Read session and guard active (not expired, not finalized)
9
- const session = readDirectSession(session_id);
10
- guardDirectSessionActive(session);
11
- // 2. Capture after snapshot
12
- const afterSnapshot = await captureRepoSnapshot(session.resolved_repo_path);
13
- // 3. Build change artifacts from before/after snapshots
14
- const changeArtifacts = await buildChangeArtifacts(session.resolved_repo_path, session.workspace_snapshot_before, afterSnapshot);
15
- // 4. Finalize session record — writes changed-files.json, diff.patch,
16
- // summary.json, summary.md and marks the session as finalized.
17
- finalizeDirectSessionRecord(session_id, afterSnapshot, changeArtifacts);
18
- // 5. Build verification summary from session.verification_runs
19
- const verification_summary = {};
20
- for (const run of session.verification_runs) {
21
- verification_summary[run.command] = run.passed ? "passed" : "failed";
22
- }
23
- // 6. Identify out-of-scope changes (paths outside resolved_repo_path)
24
- const out_of_scope_changes = findOutOfScopeChanges(changeArtifacts, session.resolved_repo_path);
25
- // 7. Build compact summary with categorized changes
26
- const sessionDir = getDirectSessionDir(session_id);
27
- return {
28
- session_id,
29
- changed_files_total: changeArtifacts.changed_files.length,
30
- source_changes: changeArtifacts.artifact_hygiene.source_changes,
31
- tracked_build_artifacts: changeArtifacts.artifact_hygiene.tracked_build_artifacts,
32
- runtime_generated_files: changeArtifacts.artifact_hygiene.runtime_generated_files,
33
- suspicious_changes: changeArtifacts.artifact_hygiene.suspicious_changes,
34
- out_of_scope_changes,
35
- verification_summary,
36
- diff_path: join(sessionDir, "diff.patch"),
37
- summary_path: join(sessionDir, "summary.md"),
38
- finalized: true,
39
- next_action: "Call audit_session to independently review the changes.",
40
- };
41
- }
42
- // ── Helpers ────────────────────────────────────────────────────────
43
- /**
44
- * Filter changeArtifacts.changed_files for paths that resolve outside the
45
- * session's resolved_repo_path and map them to ClassifiedChange format.
46
- */
47
- function findOutOfScopeChanges(artifacts, resolvedRepoPath) {
48
- const normalizedRepo = resolve(resolvedRepoPath);
49
- return artifacts.changed_files
50
- .filter((file) => {
51
- const fullPath = resolve(normalizedRepo, file.path);
52
- const rel = relative(normalizedRepo, fullPath);
53
- return isAbsolute(rel) || rel.startsWith("..");
54
- })
55
- .map((file) => toClassifiedChange(file));
56
- }
57
- /**
58
- * Map a ChangedFile to the ClassifiedChange format by adding a reason string.
59
- */
60
- function toClassifiedChange(file) {
61
- return {
62
- path: file.path,
63
- change: file.change,
64
- tracked: file.tracked,
65
- ignored: file.ignored,
66
- kind: file.kind,
67
- reason: classificationReason(file),
68
- };
69
- }
70
- /**
71
- * Produce a human-readable reason for a changed file classification.
72
- * Mirrors the logic in changeCapture.ts classifyArtifactHygiene.
73
- */
74
- function classificationReason(change) {
75
- if (change.ignored)
76
- return "untracked path is ignored by repository Git rules";
77
- if (change.kind === "build_artifact" && change.tracked)
78
- return "artifact-like path is tracked by Git and requires review";
79
- if (change.kind === "build_artifact")
80
- return "artifact-like path is not ignored and requires review";
81
- if (change.kind === "runtime_generated")
82
- return "runtime-generated path is not ignored and requires review";
83
- return change.tracked ? "tracked source change" : "untracked source change";
84
- }
@@ -1,110 +0,0 @@
1
- /**
2
- * v0.8.0: createSubgoalTask — 原子地将一个新 subgoal 关联到一个新任务。
3
- *
4
- * 流程(原子语义):
5
- * 1. 读取 goal_status.json(不存在则抛 goal_not_found)
6
- * 2. addSubgoal(校验 depends_on 引用已有 subgoal)
7
- * 3. createTask 创建关联任务(写入 goal_id / subgoal_id 到 task status.json)
8
- * 4. linkTaskToSubgoal(将 task_id 关联到 subgoal)
9
- * 5. updateSubgoalStatus(running)(ready → running)
10
- * 6. 写回 goal_status.json
11
- *
12
- * 注意:
13
- * - createSubgoalTask 强制 execution_mode 为 "execute",因为 assess_only 不产生可关联的 task。
14
- * - createTask 内部使用 getConfig(),因此 workspaceRoot 由全局配置决定。
15
- */
16
- import { readGoalStatus, writeGoalStatus } from "../goal/goalStore.js";
17
- import { addSubgoal, linkTaskToSubgoal, updateSubgoalStatus } from "../goal/goalStatus.js";
18
- import { createTask } from "./createTask.js";
19
- import { createWorktree, discardWorktree } from "../goal/worktreeManager.js";
20
- import { getConfig } from "../config.js";
21
- import { PatchWardenError } from "../errors.js";
22
- // ── 函数实现 ──────────────────────────────────────────────────────
23
- /**
24
- * 原子地创建一个 subgoal 并关联到一个新任务。
25
- *
26
- * @throws PatchWardenError("goal_not_found") 当 goal_id 不存在
27
- * @throws PatchWardenError("invalid_dependency") 当 depends_on 引用不存在的 subgoal
28
- * @throws PatchWardenError("invalid_execution_mode") 当 execution_mode 为 "assess_only"
29
- */
30
- export async function createSubgoalTask(input) {
31
- // assess_only 不产生 task,无法关联到 subgoal
32
- if (input.execution_mode === "assess_only") {
33
- throw new PatchWardenError("invalid_execution_mode", "createSubgoalTask does not support execution_mode \"assess_only\"", "Use execution_mode \"execute\" (default) so a task is created and linked to the subgoal.", true, { goal_id: input.goal_id, execution_mode: input.execution_mode });
34
- }
35
- // 1. 读取 goal_status.json,如果不存在抛 goal_not_found
36
- let goalStatus;
37
- try {
38
- goalStatus = readGoalStatus(input.goal_id);
39
- }
40
- catch {
41
- throw new PatchWardenError("goal_not_found", `Goal "${input.goal_id}" not found`, "Call list_goals to see available goals.", true, { goal_id: input.goal_id });
42
- }
43
- // 2. addSubgoal(校验 depends_on)
44
- const { goalStatus: withSubgoal, subgoalId } = addSubgoal(goalStatus, input.subgoal_title, input.depends_on ?? []);
45
- // 2.5 Worktree 隔离(默认开启):为该 subgoal task 创建独立 git worktree,
46
- // 用 worktree 路径作为 createTask 的 repo_path,避免并发 task 互相污染。
47
- // isolate_worktree === false 时退化为原行为(使用 input.repo_path)。
48
- const isolate = input.isolate_worktree !== false;
49
- let repoPathForTask = input.repo_path;
50
- let worktreeId = null;
51
- if (isolate) {
52
- const workspaceRoot = getConfig().workspaceRoot;
53
- const wt = createWorktree(input.goal_id, subgoalId, workspaceRoot);
54
- repoPathForTask = wt.worktreePath;
55
- worktreeId = wt.worktreeId;
56
- }
57
- // 3. 调用 createTask 创建关联任务(强制 execute 模式,确保返回 CreateTaskOutput)
58
- let taskId;
59
- try {
60
- const taskResult = await createTask({
61
- plan_id: input.plan_id,
62
- inline_plan: input.inline_plan,
63
- plan_title: input.plan_title,
64
- template: input.template,
65
- goal: input.goal,
66
- agent: input.agent,
67
- repo_path: repoPathForTask,
68
- test_command: input.test_command,
69
- verify_commands: input.verify_commands,
70
- timeout_seconds: input.timeout_seconds,
71
- execution_mode: "execute",
72
- assessment_id: input.assessment_id,
73
- scope: input.scope,
74
- forbidden: input.forbidden,
75
- verification: input.verification,
76
- done_evidence: input.done_evidence,
77
- goal_id: input.goal_id,
78
- subgoal_id: subgoalId,
79
- });
80
- // execution_mode 为 "execute",createTask 返回 CreateTaskOutput(含 task_id)
81
- taskId = taskResult.task_id;
82
- }
83
- catch (createTaskErr) {
84
- // createTask 失败:清理已创建的隔离 worktree,避免遗留半成品(best effort)
85
- if (isolate && worktreeId) {
86
- try {
87
- discardWorktree(worktreeId, getConfig().workspaceRoot);
88
- }
89
- catch { /* ignore cleanup failure,向上抛出原始 createTask 错误 */ }
90
- }
91
- throw createTaskErr;
92
- }
93
- // 4. linkTaskToSubgoal + updateSubgoalStatus(running)
94
- const withTask = linkTaskToSubgoal(withSubgoal, subgoalId, taskId);
95
- let withRunning = updateSubgoalStatus(withTask, subgoalId, "running");
96
- // 4.5 隔离模式下,把 worktree_id 记录到 subgoal 对象(写入 goal_status.json)
97
- if (isolate && worktreeId) {
98
- const newSubgoals = withRunning.subgoals.map((s) => s.id === subgoalId
99
- ? { ...s, worktree_id: worktreeId }
100
- : s);
101
- withRunning = { ...withRunning, subgoals: newSubgoals };
102
- }
103
- // 5. 写回 goal_status.json
104
- writeGoalStatus(input.goal_id, withRunning);
105
- return {
106
- subgoal_id: subgoalId,
107
- task_id: taskId,
108
- subgoal_status: "running",
109
- };
110
- }
@@ -1,15 +0,0 @@
1
- export declare function killTask(taskId: string): {
2
- task_id: string;
3
- previous_status: string;
4
- new_status: string;
5
- message: string;
6
- cancel_requested?: undefined;
7
- force_kill_requested?: undefined;
8
- } | {
9
- task_id: string;
10
- previous_status: string;
11
- new_status: string;
12
- cancel_requested: boolean;
13
- force_kill_requested: boolean;
14
- message: string;
15
- };
@@ -1,12 +0,0 @@
1
- export interface AgentAvailability {
2
- name: string;
3
- configured: true;
4
- available: boolean;
5
- command: string;
6
- reason: string | null;
7
- checked_at: string;
8
- }
9
- export declare function listAgents(): {
10
- agents: AgentAvailability[];
11
- total: number;
12
- };
@@ -1,42 +0,0 @@
1
- import { existsSync, statSync } from "node:fs";
2
- import { basename, delimiter, extname, isAbsolute, join } from "node:path";
3
- import { getConfig } from "../config.js";
4
- export function listAgents() {
5
- const config = getConfig();
6
- const checkedAt = new Date().toISOString();
7
- const agents = Object.entries(config.agents)
8
- .sort(([left], [right]) => left.localeCompare(right))
9
- .map(([name, agent]) => {
10
- const available = commandExists(agent.command);
11
- return {
12
- name,
13
- configured: true,
14
- available,
15
- command: basename(agent.command),
16
- reason: available ? null : "Configured executable was not found on disk or PATH.",
17
- checked_at: checkedAt,
18
- };
19
- });
20
- return { agents, total: agents.length };
21
- }
22
- function commandExists(command) {
23
- if (isAbsolute(command) || command.includes("/") || command.includes("\\")) {
24
- return isFile(command);
25
- }
26
- const pathEntries = (process.env.PATH || "").split(delimiter).filter(Boolean);
27
- const extensions = process.platform === "win32"
28
- ? (process.env.PATHEXT || ".EXE;.CMD;.BAT;.COM").split(";")
29
- : [""];
30
- const candidates = extname(command)
31
- ? pathEntries.map((entry) => join(entry, command))
32
- : pathEntries.flatMap((entry) => extensions.map((extension) => join(entry, `${command}${extension}`)));
33
- return candidates.some(isFile);
34
- }
35
- function isFile(path) {
36
- try {
37
- return existsSync(path) && statSync(path).isFile();
38
- }
39
- catch {
40
- return false;
41
- }
42
- }
@@ -1,352 +0,0 @@
1
- import { existsSync, readdirSync, readFileSync, writeFileSync, renameSync, unlinkSync, mkdirSync, statSync, } from "node:fs";
2
- import { join, resolve, dirname } from "node:path";
3
- import { getTasksDir, getConfig } from "../config.js";
4
- import { diagnoseTask } from "./diagnoseTask.js";
5
- import { syncSubgoalOnTaskDone, readTaskGoalMeta } from "../goal/subgoalSync.js";
6
- // ── Defaults ────────────────────────────────────────────────────────
7
- const DEFAULT_MAX_AGE_MINUTES = 30;
8
- const RECONCILE_LOG_NAME = "reconcile.log";
9
- // ── Main entry point ──────────────────────────────────────────────
10
- export function reconcileTasks(input = {}, config = getConfig()) {
11
- const mode = input.mode === "safe_fix" ? "safe_fix" : "report_only";
12
- const maxAgeMinutes = typeof input.max_age_minutes === "number" && input.max_age_minutes > 0
13
- ? Math.min(input.max_age_minutes, 24 * 60)
14
- : DEFAULT_MAX_AGE_MINUTES;
15
- const includeDoneCandidates = input.include_done_candidates !== false; // default true
16
- const maxAgeSeconds = maxAgeMinutes * 60;
17
- const tasksDir = getTasksDir(config);
18
- const reports = [];
19
- let scanned = 0;
20
- let candidates = 0;
21
- let reconciled = 0;
22
- let skippedLowConfidence = 0;
23
- let skippedActiveWatcher = 0;
24
- const nowMs = Date.now();
25
- if (!existsSync(tasksDir)) {
26
- return {
27
- mode,
28
- scanned: 0,
29
- candidates: 0,
30
- reconciled: 0,
31
- skipped_low_confidence: 0,
32
- skipped_active_watcher: 0,
33
- reports: [],
34
- reconcile_log_path: null,
35
- };
36
- }
37
- let entries = [];
38
- try {
39
- entries = readdirSync(tasksDir, { withFileTypes: true });
40
- }
41
- catch {
42
- return {
43
- mode,
44
- scanned: 0,
45
- candidates: 0,
46
- reconciled: 0,
47
- skipped_low_confidence: 0,
48
- skipped_active_watcher: 0,
49
- reports: [],
50
- reconcile_log_path: null,
51
- };
52
- }
53
- const taskDirs = entries.filter((e) => e.isDirectory());
54
- for (const entry of taskDirs) {
55
- scanned += 1;
56
- const taskId = entry.name;
57
- const taskDir = resolve(tasksDir, taskId);
58
- const statusFile = join(taskDir, "status.json");
59
- if (!existsSync(statusFile))
60
- continue;
61
- let statusData;
62
- try {
63
- statusData = JSON.parse(readFileSync(statusFile, "utf-8"));
64
- }
65
- catch {
66
- continue; // corrupted status, skip
67
- }
68
- const statusStr = typeof statusData.status === "string" ? statusData.status : "unknown";
69
- // Filter: only running / collecting_artifacts / (optionally) done_by_agent candidates
70
- const isCandidate = statusStr === "running" ||
71
- statusStr === "collecting_artifacts" ||
72
- (includeDoneCandidates && statusStr === "done_by_agent");
73
- if (!isCandidate)
74
- continue;
75
- // Filter by age — use the oldest reliable timestamp on the task
76
- const ageSeconds = taskAgeSeconds(taskDir, statusData, nowMs);
77
- if (ageSeconds !== null && ageSeconds < maxAgeSeconds)
78
- continue;
79
- candidates += 1;
80
- // Diagnose the task
81
- let diagnosis;
82
- try {
83
- diagnosis = diagnoseTask({ task_id: taskId }, config);
84
- }
85
- catch {
86
- continue; // diagnosis failed — skip
87
- }
88
- const report = {
89
- task_id: taskId,
90
- status: diagnosis.status,
91
- phase: diagnosis.phase,
92
- diagnosis: diagnosis.diagnosis,
93
- confidence: diagnosis.confidence,
94
- reasons: diagnosis.reasons,
95
- safe_actions: diagnosis.safe_actions,
96
- age_seconds: ageSeconds,
97
- action_taken: "left_unchanged",
98
- previous_status: null,
99
- new_status: null,
100
- applied_at: null,
101
- applied_by: null,
102
- evidence_summary: {
103
- heartbeat_age_seconds: diagnosis.evidence.heartbeat_age_seconds,
104
- stdout_age_seconds: diagnosis.evidence.stdout_age_seconds,
105
- child_pid: diagnosis.evidence.child_pid,
106
- child_pid_alive: diagnosis.evidence.child_pid_alive,
107
- watcher_owns_task: diagnosis.evidence.watcher_owns_task,
108
- },
109
- };
110
- // ── safe_fix rules ──
111
- //
112
- // safe_fix is ONLY applied when:
113
- // 1. mode === "safe_fix"
114
- // 2. diagnosis.confidence === "high"
115
- // 3. The task is NOT still owned by an active watcher (we must not
116
- // touch tasks the live watcher is executing).
117
- // 4. The diagnosis type maps to a reconcilable action.
118
- //
119
- // Anything else is left_unchanged and recorded for audit.
120
- if (mode === "safe_fix" && diagnosis.confidence === "high") {
121
- if (diagnosis.evidence.watcher_owns_task) {
122
- // Hard rule: do not touch tasks the live watcher still owns.
123
- skippedActiveWatcher += 1;
124
- report.reasons = [
125
- ...report.reasons,
126
- "safe_fix skipped: task is still owned by an active watcher instance",
127
- ];
128
- }
129
- else {
130
- const fixResult = applySafeFix(taskDir, taskId, statusData, diagnosis.diagnosis, diagnosis.reasons, diagnosis.evidence, config);
131
- if (fixResult.applied) {
132
- report.action_taken = fixResult.action_taken;
133
- report.previous_status = fixResult.previous_status;
134
- report.new_status = fixResult.new_status;
135
- report.applied_at = fixResult.applied_at;
136
- report.applied_by = "reconcile_tasks";
137
- reconciled += 1;
138
- }
139
- else {
140
- // Diagnosis type does not map to a safe_fix action — leave unchanged
141
- report.reasons = [
142
- ...report.reasons,
143
- `safe_fix skipped: diagnosis "${diagnosis.diagnosis}" has no automatic safe_fix action`,
144
- ];
145
- skippedLowConfidence += 1;
146
- }
147
- }
148
- }
149
- else if (mode === "safe_fix" && diagnosis.confidence !== "high") {
150
- skippedLowConfidence += 1;
151
- report.reasons = [
152
- ...report.reasons,
153
- `safe_fix skipped: confidence is "${diagnosis.confidence}", only "high" is eligible`,
154
- ];
155
- }
156
- if (reports.length < 200) {
157
- reports.push(report);
158
- }
159
- if (reports.length >= 200)
160
- break;
161
- }
162
- // ── Write reconcile.log when safe_fix applied any change ──
163
- let reconcileLogPath = null;
164
- if (mode === "safe_fix" && reconciled > 0) {
165
- reconcileLogPath = writeReconcileLog(tasksDir, config, reports.filter((r) => r.action_taken !== "left_unchanged"));
166
- }
167
- return {
168
- mode,
169
- scanned,
170
- candidates,
171
- reconciled,
172
- skipped_low_confidence: skippedLowConfidence,
173
- skipped_active_watcher: skippedActiveWatcher,
174
- reports,
175
- reconcile_log_path: reconcileLogPath,
176
- };
177
- }
178
- function applySafeFix(taskDir, taskId, currentStatus, diagnosis, reasons, evidence, config) {
179
- // Map diagnosis type to new status. Only high-confidence, well-understood
180
- // diagnoses are eligible. "possibly_stale_running" and "unknown" are NOT
181
- // eligible even if confidence were high (which they never are).
182
- const previousStatus = typeof currentStatus.status === "string" ? currentStatus.status : "unknown";
183
- let newStatus = null;
184
- let actionTaken = "left_unchanged";
185
- switch (diagnosis) {
186
- case "stale_running":
187
- newStatus = "failed_stale";
188
- actionTaken = "marked_failed_stale";
189
- break;
190
- case "orphaned_running":
191
- newStatus = "orphaned";
192
- actionTaken = "marked_orphaned";
193
- break;
194
- case "done_candidate":
195
- newStatus = "done_by_agent";
196
- actionTaken = "marked_done_by_agent";
197
- break;
198
- case "artifact_collection_stuck":
199
- // Treat as failed_stale — artifact collection should not hang.
200
- newStatus = "failed_stale";
201
- actionTaken = "marked_failed_stale";
202
- break;
203
- default:
204
- // active_running, possibly_stale_running, unknown, terminal — not eligible
205
- return {
206
- applied: false,
207
- action_taken: "left_unchanged",
208
- previous_status: previousStatus,
209
- new_status: previousStatus,
210
- applied_at: new Date().toISOString(),
211
- };
212
- }
213
- const appliedAt = new Date().toISOString();
214
- const statusFile = join(taskDir, "status.json");
215
- const backupFile = join(taskDir, "status.json.bak");
216
- // 1. Write status.json.bak with current contents (overwrite any prior backup)
217
- 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);
273
- }
274
- }
275
- catch { /* ignore */ }
276
- return {
277
- applied: false,
278
- action_taken: "left_unchanged",
279
- previous_status: previousStatus,
280
- new_status: previousStatus,
281
- applied_at: appliedAt,
282
- };
283
- }
284
- // v0.8.0: 当状态变为 done_by_agent 时,同步关联 subgoal 状态(running → done_by_agent)
285
- if (newStatus === "done_by_agent") {
286
- const goalMeta = readTaskGoalMeta(taskDir);
287
- if (goalMeta.subgoal_id) {
288
- syncSubgoalOnTaskDone(taskId, goalMeta, config.workspaceRoot);
289
- }
290
- }
291
- return {
292
- applied: true,
293
- action_taken: actionTaken,
294
- previous_status: previousStatus,
295
- new_status: newStatus,
296
- applied_at: appliedAt,
297
- };
298
- }
299
- // ── reconcile.log writer ───────────────────────────────────────────
300
- function writeReconcileLog(tasksDir, config, appliedReports) {
301
- // The reconcile.log lives at the .patchwarden/ root (parent of tasksDir),
302
- // so it captures every reconcile run across all tasks.
303
- const logDir = dirname(tasksDir);
304
- const logPath = join(logDir, RECONCILE_LOG_NAME);
305
- try {
306
- mkdirSync(logDir, { recursive: true });
307
- }
308
- catch { /* ignore */ }
309
- const lines = [];
310
- for (const report of appliedReports) {
311
- lines.push(JSON.stringify({
312
- timestamp: report.applied_at,
313
- task_id: report.task_id,
314
- previous_status: report.previous_status,
315
- new_status: report.new_status,
316
- diagnosis: report.diagnosis,
317
- confidence: report.confidence,
318
- applied_by: report.applied_by,
319
- reasons: report.reasons,
320
- evidence: report.evidence_summary,
321
- }));
322
- }
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
- try {
327
- writeFileSync(logPath, nextContent, "utf-8");
328
- }
329
- catch {
330
- // If we cannot write the log, the status change still happened —
331
- // the status.json itself contains the diagnosis audit fields.
332
- }
333
- return logPath;
334
- }
335
- // ── Task age helper ────────────────────────────────────────────────
336
- function taskAgeSeconds(taskDir, status, nowMs) {
337
- // Prefer created_at; fall back to status.json mtime.
338
- const createdStr = typeof status.created_at === "string" ? status.created_at : null;
339
- if (createdStr) {
340
- const ms = Date.parse(createdStr);
341
- if (Number.isFinite(ms)) {
342
- return Math.max(0, Math.round((nowMs - ms) / 1000));
343
- }
344
- }
345
- try {
346
- const stat = statSync(join(taskDir, "status.json"));
347
- return Math.max(0, Math.round((nowMs - stat.mtimeMs) / 1000));
348
- }
349
- catch {
350
- return null;
351
- }
352
- }
@@ -1,45 +0,0 @@
1
- import { readFileSync, writeFileSync, 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 data = JSON.parse(readFileSync(statusFile, "utf-8"));
16
- if (!data.plan_id || !data.agent) {
17
- throw new Error(`Task "${taskId}" is missing plan_id or agent. Cannot retry.`);
18
- }
19
- // Create a new task with the same parameters
20
- const newTask = await createTask({
21
- plan_id: data.plan_id,
22
- agent: data.agent,
23
- repo_path: data.repo_path,
24
- test_command: data.test_command,
25
- verify_commands: data.verify_commands,
26
- timeout_seconds: data.timeout_seconds,
27
- });
28
- // Record retry relationship in the new task
29
- const newStatusFile = join(newTask.path, "status.json");
30
- const newData = JSON.parse(readFileSync(newStatusFile, "utf-8"));
31
- newData.retry_of = taskId;
32
- newData.retry_count = (data.retry_count || 0) + 1;
33
- newData.plan_source = data.plan_source || "saved";
34
- newData.template = data.template || null;
35
- newData.change_policy = data.change_policy || "repo_scoped_changes";
36
- writeFileSync(newStatusFile, JSON.stringify(newData, null, 2), "utf-8");
37
- return {
38
- original_task_id: taskId,
39
- new_task_id: newTask.task_id,
40
- plan_id: newTask.plan_id,
41
- agent: newTask.agent,
42
- status: newTask.status,
43
- message: `New task created from retry of ${taskId}. Original task is unchanged.`,
44
- };
45
- }