patchwarden 1.5.0 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (597) hide show
  1. package/PatchWarden.cmd +32 -2
  2. package/README.en.md +194 -26
  3. package/README.md +160 -22
  4. package/dist/assessments/agentAssessor.d.ts +1 -1
  5. package/dist/assessments/agentAssessor.js +24 -15
  6. package/dist/assessments/assessmentStore.d.ts +1 -1
  7. package/dist/assessments/assessmentStore.js +44 -36
  8. package/dist/assessments/confirmCli.js +5 -4
  9. package/dist/config.d.ts +22 -0
  10. package/dist/config.js +98 -11
  11. package/dist/control/fileManager.d.ts +2 -0
  12. package/dist/control/fileManager.js +39 -0
  13. package/dist/control/middleware/auth.d.ts +16 -0
  14. package/dist/control/middleware/auth.js +17 -0
  15. package/dist/control/middleware/static.d.ts +3 -0
  16. package/dist/control/middleware/static.js +78 -0
  17. package/dist/control/routeTable.d.ts +24 -0
  18. package/dist/control/routeTable.js +263 -0
  19. package/dist/control/routes/audit.d.ts +15 -0
  20. package/dist/control/routes/audit.js +287 -0
  21. package/dist/control/routes/evidence.d.ts +4 -0
  22. package/dist/control/routes/evidence.js +96 -0
  23. package/dist/control/routes/lineage.d.ts +3 -0
  24. package/dist/control/routes/lineage.js +71 -0
  25. package/dist/control/routes/policy.d.ts +3 -0
  26. package/dist/control/routes/policy.js +81 -0
  27. package/dist/control/routes/process.d.ts +12 -0
  28. package/dist/control/routes/process.js +487 -0
  29. package/dist/control/routes/sessions.d.ts +30 -0
  30. package/dist/control/routes/sessions.js +287 -0
  31. package/dist/control/routes/status.d.ts +51 -0
  32. package/dist/control/routes/status.js +307 -0
  33. package/dist/control/routes/taskActions.d.ts +21 -0
  34. package/dist/control/routes/taskActions.js +197 -0
  35. package/dist/control/routes/tasks.d.ts +24 -0
  36. package/dist/control/routes/tasks.js +309 -0
  37. package/dist/control/routes/workspace.d.ts +15 -0
  38. package/dist/control/routes/workspace.js +220 -0
  39. package/dist/control/runtime.d.ts +92 -0
  40. package/dist/control/runtime.js +425 -0
  41. package/dist/control/server.d.ts +13 -0
  42. package/dist/control/server.js +150 -0
  43. package/dist/control/shared.d.ts +59 -0
  44. package/dist/control/shared.js +341 -0
  45. package/dist/controlCenter.d.ts +6 -0
  46. package/dist/controlCenter.js +7 -2197
  47. package/dist/direct/directAudit.js +234 -225
  48. package/dist/direct/directGuards.d.ts +2 -1
  49. package/dist/direct/directGuards.js +72 -37
  50. package/dist/direct/directPatch.d.ts +6 -1
  51. package/dist/direct/directPatch.js +55 -8
  52. package/dist/direct/directSessionStore.d.ts +16 -6
  53. package/dist/direct/directSessionStore.js +127 -35
  54. package/dist/direct/directVerification.js +8 -16
  55. package/dist/doctor.d.ts +18 -1
  56. package/dist/doctor.js +614 -373
  57. package/dist/goal/acceptanceEngine.d.ts +1 -1
  58. package/dist/goal/goalProgress.js +75 -69
  59. package/dist/goal/goalReport.d.ts +54 -0
  60. package/dist/goal/goalReport.js +197 -0
  61. package/dist/goal/goalStatus.d.ts +8 -0
  62. package/dist/goal/goalStatus.js +10 -3
  63. package/dist/goal/goalStore.d.ts +9 -2
  64. package/dist/goal/goalStore.js +152 -45
  65. package/dist/goal/handoffExport.js +6 -6
  66. package/dist/goal/specKitImport.d.ts +63 -0
  67. package/dist/goal/specKitImport.js +221 -0
  68. package/dist/goal/subgoalSync.js +13 -10
  69. package/dist/goal/worktreeManager.d.ts +2 -0
  70. package/dist/goal/worktreeManager.js +209 -136
  71. package/dist/httpServer.js +126 -44
  72. package/dist/index.js +7 -4
  73. package/dist/logging.d.ts +7 -1
  74. package/dist/logging.js +13 -3
  75. package/dist/policy/projectPolicy.js +5 -2
  76. package/dist/release/releaseGate.d.ts +1 -1
  77. package/dist/release/releaseGate.js +78 -25
  78. package/dist/runner/agentInvocation.d.ts +14 -0
  79. package/dist/runner/agentInvocation.js +96 -4
  80. package/dist/runner/changeCapture.d.ts +14 -6
  81. package/dist/runner/changeCapture.js +287 -81
  82. package/dist/runner/cli.js +7 -6
  83. package/dist/runner/postTaskCleanup.js +60 -7
  84. package/dist/runner/processSecurity.d.ts +56 -0
  85. package/dist/runner/processSecurity.js +373 -0
  86. package/dist/runner/runTask.d.ts +1 -1
  87. package/dist/runner/runTask.js +339 -287
  88. package/dist/runner/simpleProcess.d.ts +3 -0
  89. package/dist/runner/simpleProcess.js +52 -38
  90. package/dist/{taskProgress.d.ts → runner/taskProgress.d.ts} +1 -1
  91. package/dist/{taskProgress.js → runner/taskProgress.js} +2 -2
  92. package/dist/{taskRuntime.d.ts → runner/taskRuntime.d.ts} +1 -1
  93. package/dist/{taskRuntime.js → runner/taskRuntime.js} +3 -2
  94. package/dist/runner/taskStatusStore.d.ts +11 -0
  95. package/dist/runner/taskStatusStore.js +24 -0
  96. package/dist/runner/watch.d.ts +14 -1
  97. package/dist/runner/watch.js +265 -54
  98. package/dist/security/contentRedaction.d.ts +6 -0
  99. package/dist/security/contentRedaction.js +24 -2
  100. package/dist/security/discoveryTokenStore.d.ts +3 -2
  101. package/dist/security/discoveryTokenStore.js +17 -2
  102. package/dist/security/loopbackHost.d.ts +2 -0
  103. package/dist/security/loopbackHost.js +16 -0
  104. package/dist/security/pathGuard.js +32 -17
  105. package/dist/security/planGuard.js +2 -1
  106. package/dist/security/runtimeGuard.js +9 -6
  107. package/dist/security/secretComparison.d.ts +3 -0
  108. package/dist/security/secretComparison.js +11 -0
  109. package/dist/security/sensitiveGuard.d.ts +5 -0
  110. package/dist/security/sensitiveGuard.js +27 -8
  111. package/dist/security/toolInvocationGuard.d.ts +2 -2
  112. package/dist/security/toolInvocationGuard.js +1 -1
  113. package/dist/security/workspaceRootGuard.d.ts +8 -0
  114. package/dist/security/workspaceRootGuard.js +47 -0
  115. package/dist/smoke-test.js +280 -273
  116. package/dist/tools/catalog/index.d.ts +4 -0
  117. package/dist/tools/catalog/index.js +4 -0
  118. package/dist/tools/{toolCatalog.js → catalog/toolCatalog.js} +3 -12
  119. package/dist/tools/{toolRegistry.d.ts → catalog/toolRegistry.d.ts} +3 -3
  120. package/dist/tools/{toolRegistry.js → catalog/toolRegistry.js} +28 -15
  121. package/dist/tools/definitions/toolDefs.d.ts +18 -0
  122. package/dist/tools/definitions/toolDefs.js +1177 -0
  123. package/dist/tools/{auditSession.d.ts → diagnostics/auditSession.d.ts} +1 -1
  124. package/dist/tools/{auditSession.js → diagnostics/auditSession.js} +2 -2
  125. package/dist/tools/{auditTask.d.ts → diagnostics/auditTask.d.ts} +8 -63
  126. package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +111 -37
  127. package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +6 -6
  128. package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +10 -10
  129. package/dist/tools/diagnostics/index.d.ts +6 -0
  130. package/dist/tools/diagnostics/index.js +6 -0
  131. package/dist/tools/{safeStatus.d.ts → diagnostics/safeStatus.d.ts} +3 -3
  132. package/dist/tools/{safeStatus.js → diagnostics/safeStatus.js} +6 -6
  133. package/dist/tools/{safeViews.d.ts → diagnostics/safeViews.d.ts} +14 -10
  134. package/dist/tools/{safeViews.js → diagnostics/safeViews.js} +25 -15
  135. package/dist/tools/{schemaDriftCheck.d.ts → diagnostics/schemaDriftCheck.d.ts} +1 -1
  136. package/dist/tools/{schemaDriftCheck.js → diagnostics/schemaDriftCheck.js} +2 -2
  137. package/dist/tools/{createDirectSession.d.ts → direct/createDirectSession.d.ts} +1 -1
  138. package/dist/tools/{createDirectSession.js → direct/createDirectSession.js} +8 -8
  139. package/dist/tools/{finalizeDirectSession.d.ts → direct/finalizeDirectSession.d.ts} +2 -2
  140. package/dist/tools/direct/finalizeDirectSession.js +86 -0
  141. package/dist/tools/direct/index.d.ts +3 -0
  142. package/dist/tools/direct/index.js +3 -0
  143. package/dist/tools/{runDirectVerificationBundle.js → direct/runDirectVerificationBundle.js} +2 -2
  144. package/dist/tools/{discoverTools.d.ts → discovery/discoverTools.d.ts} +2 -2
  145. package/dist/tools/{discoverTools.js → discovery/discoverTools.js} +5 -5
  146. package/dist/tools/{explainTool.d.ts → discovery/explainTool.d.ts} +2 -2
  147. package/dist/tools/{explainTool.js → discovery/explainTool.js} +2 -2
  148. package/dist/tools/discovery/index.d.ts +3 -0
  149. package/dist/tools/discovery/index.js +3 -0
  150. package/dist/tools/{invokeDiscoveredTool.d.ts → discovery/invokeDiscoveredTool.d.ts} +2 -2
  151. package/dist/tools/{invokeDiscoveredTool.js → discovery/invokeDiscoveredTool.js} +5 -5
  152. package/dist/tools/dispatch/coreDispatch.d.ts +9 -0
  153. package/dist/tools/dispatch/coreDispatch.js +283 -0
  154. package/dist/tools/dispatch/diagnosticDispatch.d.ts +12 -0
  155. package/dist/tools/dispatch/diagnosticDispatch.js +80 -0
  156. package/dist/tools/dispatch/directDispatch.d.ts +8 -0
  157. package/dist/tools/dispatch/directDispatch.js +116 -0
  158. package/dist/tools/dispatch/goalDispatch.d.ts +8 -0
  159. package/dist/tools/dispatch/goalDispatch.js +92 -0
  160. package/dist/tools/dispatch/releaseDispatch.d.ts +8 -0
  161. package/dist/tools/dispatch/releaseDispatch.js +46 -0
  162. package/dist/tools/dispatch/types.d.ts +23 -0
  163. package/dist/tools/dispatch/types.js +15 -0
  164. package/dist/tools/dispatch/validation.d.ts +7 -0
  165. package/dist/tools/dispatch/validation.js +71 -0
  166. package/dist/tools/{getPlan.js → goals/getPlan.js} +4 -4
  167. package/dist/tools/{goalSubgoalTask.d.ts → goals/goalSubgoalTask.d.ts} +2 -2
  168. package/dist/tools/goals/goalSubgoalTask.js +118 -0
  169. package/dist/tools/goals/index.d.ts +3 -0
  170. package/dist/tools/goals/index.js +3 -0
  171. package/dist/tools/{savePlan.js → goals/savePlan.js} +28 -8
  172. package/dist/tools/registry.d.ts +10 -14
  173. package/dist/tools/registry.js +47 -1651
  174. package/dist/tools/{checkReleaseGate.d.ts → release/checkReleaseGate.d.ts} +1 -1
  175. package/dist/tools/{checkReleaseGate.js → release/checkReleaseGate.js} +3 -3
  176. package/dist/tools/release/index.d.ts +2 -0
  177. package/dist/tools/release/index.js +2 -0
  178. package/dist/tools/{releaseMode.d.ts → release/releaseMode.d.ts} +2 -2
  179. package/dist/tools/{releaseMode.js → release/releaseMode.js} +46 -26
  180. package/dist/tools/tasks/cancelTask.d.ts +11 -0
  181. package/dist/tools/tasks/cancelTask.js +85 -0
  182. package/dist/tools/{createTask.d.ts → tasks/createTask.d.ts} +12 -4
  183. package/dist/tools/{createTask.js → tasks/createTask.js} +46 -31
  184. package/dist/tools/{diagnoseTask.d.ts → tasks/diagnoseTask.d.ts} +1 -1
  185. package/dist/tools/{diagnoseTask.js → tasks/diagnoseTask.js} +8 -8
  186. package/dist/tools/{evidencePack.d.ts → tasks/evidencePack.d.ts} +6 -0
  187. package/dist/tools/tasks/evidencePack.js +387 -0
  188. package/dist/tools/{getTaskFile.js → tasks/getTaskFile.js} +4 -4
  189. package/dist/tools/{getTaskStatus.d.ts → tasks/getTaskStatus.d.ts} +6 -1
  190. package/dist/tools/{getTaskStatus.js → tasks/getTaskStatus.js} +5 -5
  191. package/dist/tools/{getTaskStdoutTail.js → tasks/getTaskStdoutTail.js} +13 -11
  192. package/dist/tools/{getTaskSummary.js → tasks/getTaskSummary.js} +33 -26
  193. package/dist/tools/tasks/index.d.ts +18 -0
  194. package/dist/tools/tasks/index.js +18 -0
  195. package/dist/tools/tasks/killTask.d.ts +1 -0
  196. package/dist/tools/{listTasks.d.ts → tasks/listTasks.d.ts} +1 -1
  197. package/dist/tools/{listTasks.js → tasks/listTasks.js} +3 -3
  198. package/dist/tools/{reconcileTasks.d.ts → tasks/reconcileTasks.d.ts} +1 -1
  199. package/dist/tools/{reconcileTasks.js → tasks/reconcileTasks.js} +64 -66
  200. package/dist/tools/{retryTask.d.ts → tasks/retryTask.d.ts} +2 -2
  201. package/dist/tools/tasks/retryTask.js +60 -0
  202. package/dist/tools/{runTaskLoop.d.ts → tasks/runTaskLoop.d.ts} +6 -6
  203. package/dist/tools/{runTaskLoop.js → tasks/runTaskLoop.js} +59 -31
  204. package/dist/tools/tasks/runVerification.js +38 -0
  205. package/dist/tools/{taskLineage.js → tasks/taskLineage.js} +8 -7
  206. package/dist/tools/{taskOutputs.js → tasks/taskOutputs.js} +17 -16
  207. package/dist/tools/{androidDoctor.js → workspace/androidDoctor.js} +16 -10
  208. package/dist/tools/{applyPatch.d.ts → workspace/applyPatch.d.ts} +1 -1
  209. package/dist/tools/workspace/applyPatch.js +53 -0
  210. package/dist/tools/{discardWorktree.js → workspace/discardWorktree.js} +3 -3
  211. package/dist/tools/workspace/index.d.ts +10 -0
  212. package/dist/tools/workspace/index.js +10 -0
  213. package/dist/tools/{listAgents.d.ts → workspace/listAgents.d.ts} +5 -0
  214. package/dist/tools/{listAgents.js → workspace/listAgents.js} +8 -4
  215. package/dist/tools/{listWorkspace.js → workspace/listWorkspace.js} +3 -3
  216. package/dist/tools/{mergeWorktree.js → workspace/mergeWorktree.js} +3 -3
  217. package/dist/tools/{readWorkspaceFile.js → workspace/readWorkspaceFile.js} +8 -8
  218. package/dist/tools/{recommendAgentForTask.js → workspace/recommendAgentForTask.js} +3 -3
  219. package/dist/tools/{searchWorkspace.js → workspace/searchWorkspace.js} +5 -5
  220. package/dist/tools/{syncFile.d.ts → workspace/syncFile.d.ts} +1 -1
  221. package/dist/tools/workspace/syncFile.js +134 -0
  222. package/dist/utils/atomicFile.d.ts +7 -0
  223. package/dist/utils/atomicFile.js +48 -0
  224. package/dist/utils/boundedFile.d.ts +8 -0
  225. package/dist/utils/boundedFile.js +93 -0
  226. package/dist/utils/lockedJsonFile.d.ts +15 -0
  227. package/dist/utils/lockedJsonFile.js +224 -0
  228. package/dist/utils/platform.d.ts +11 -0
  229. package/dist/utils/platform.js +26 -0
  230. package/dist/utils/stableJson.d.ts +2 -0
  231. package/dist/utils/stableJson.js +12 -0
  232. package/dist/version.d.ts +3 -2
  233. package/dist/version.js +3 -2
  234. package/dist/watcherStatus.d.ts +2 -2
  235. package/dist/watcherStatus.js +18 -0
  236. package/docs/CODE_WIKI.md +1648 -0
  237. package/docs/agentseal-integration.md +150 -0
  238. package/docs/architecture.md +23 -0
  239. package/docs/assets/patchwarden-desktop-onboarding.png +0 -0
  240. package/docs/assets/patchwarden-oss-demo.gif +0 -0
  241. package/docs/control-center/README.md +4 -4
  242. package/docs/dashboard-overview.md +86 -0
  243. package/docs/demo.md +8 -0
  244. package/docs/desktop-app.md +175 -0
  245. package/docs/direct-session-workflow.md +98 -0
  246. package/docs/evidence-pack-schema.md +215 -0
  247. package/docs/lineage-evidence-pack-workflow.md +127 -0
  248. package/docs/mcp-inspector-testing.md +200 -0
  249. package/docs/open-source-application.md +168 -0
  250. package/docs/opencode-worker.md +151 -0
  251. package/docs/openhands-worker.md +181 -0
  252. package/docs/release-checklist.md +1 -1
  253. package/docs/release-evidence.md +82 -0
  254. package/docs/spec-kit-integration.md +131 -0
  255. package/docs/task-safe-review-workflow.md +98 -0
  256. package/docs/threat-model.md +97 -0
  257. package/docs/user-feedback.md +40 -0
  258. package/docs/why-patchwarden.md +110 -0
  259. package/examples/config.example.json +4 -2
  260. package/package.json +28 -11
  261. package/scripts/README.md +15 -6
  262. package/scripts/build.js +11 -0
  263. package/scripts/checks/brand-check.js +3 -0
  264. package/scripts/checks/build-output-check.js +37 -0
  265. package/scripts/checks/control-center-smoke.js +447 -6
  266. package/scripts/checks/control-smoke.js +19 -8
  267. package/scripts/checks/http-mcp-smoke.js +82 -4
  268. package/scripts/checks/lifecycle-smoke.js +39 -33
  269. package/scripts/checks/mcp-manifest-check.js +1 -1
  270. package/scripts/checks/mcp-smoke.js +30 -4
  271. package/scripts/checks/package-manifest-check.js +20 -7
  272. package/scripts/checks/unit-tests.js +3 -5
  273. package/scripts/checks/watcher-supervisor-smoke.js +9 -0
  274. package/scripts/control/get-patchwarden-health.ps1 +1 -1
  275. package/scripts/control/manage-patchwarden.ps1 +69 -4
  276. package/scripts/control/provision-patchwarden-tunnel.ps1 +127 -0
  277. package/scripts/control/run-background-supervisor.ps1 +41 -0
  278. package/scripts/control/start-patchwarden-tunnel.ps1 +126 -32
  279. package/scripts/generate-demo-gif.py +320 -0
  280. package/{PatchWarden-Control-Tray.cmd → scripts/launchers/PatchWarden-Control-Tray.cmd} +3 -3
  281. package/scripts/launchers/PatchWarden-Control.cmd +6 -0
  282. package/{PatchWarden-Desktop.cmd → scripts/launchers/PatchWarden-Desktop.cmd} +2 -2
  283. package/scripts/launchers/Restart-PatchWarden-Control.cmd +6 -0
  284. package/{Stop-PatchWarden.cmd → scripts/launchers/Stop-PatchWarden.cmd} +2 -2
  285. package/scripts/lib/clean-generated-output.js +11 -0
  286. package/scripts/lib/file-discovery.js +12 -0
  287. package/scripts/release/desktop-preflight.js +211 -0
  288. package/scripts/release/pack-clean.js +59 -14
  289. package/src/assessments/agentAssessor.ts +29 -15
  290. package/src/assessments/assessmentStore.ts +55 -43
  291. package/src/assessments/confirmCli.ts +5 -4
  292. package/src/config.ts +105 -11
  293. package/src/control/fileManager.ts +40 -0
  294. package/src/control/middleware/auth.ts +35 -0
  295. package/src/control/middleware/static.ts +84 -0
  296. package/src/control/routeTable.ts +301 -0
  297. package/src/control/routes/audit.ts +329 -0
  298. package/src/control/routes/evidence.ts +107 -0
  299. package/src/control/routes/lineage.ts +92 -0
  300. package/src/control/routes/policy.ts +81 -0
  301. package/src/control/routes/process.ts +505 -0
  302. package/src/control/routes/sessions.ts +314 -0
  303. package/src/control/routes/status.ts +405 -0
  304. package/src/control/routes/taskActions.ts +214 -0
  305. package/src/control/routes/tasks.ts +331 -0
  306. package/src/control/routes/workspace.ts +234 -0
  307. package/src/control/runtime.ts +508 -0
  308. package/src/control/server.ts +165 -0
  309. package/src/control/shared.ts +360 -0
  310. package/src/controlCenter.ts +7 -2347
  311. package/src/direct/directAudit.ts +17 -9
  312. package/src/direct/directGuards.ts +100 -51
  313. package/src/direct/directPatch.ts +95 -8
  314. package/src/direct/directSessionStore.ts +211 -55
  315. package/src/direct/directVerification.ts +9 -23
  316. package/src/doctor.ts +766 -492
  317. package/src/goal/acceptanceEngine.ts +1 -1
  318. package/src/goal/goalProgress.ts +17 -13
  319. package/src/goal/goalReport.ts +263 -0
  320. package/src/goal/goalStatus.ts +25 -3
  321. package/src/goal/goalStore.ts +241 -54
  322. package/src/goal/handoffExport.ts +6 -6
  323. package/src/goal/specKitImport.ts +355 -0
  324. package/src/goal/subgoalSync.ts +15 -10
  325. package/src/goal/worktreeManager.ts +139 -22
  326. package/src/httpServer.ts +130 -44
  327. package/src/index.ts +7 -4
  328. package/src/logging.ts +20 -4
  329. package/src/policy/projectPolicy.ts +5 -2
  330. package/src/release/releaseGate.ts +74 -27
  331. package/src/runner/agentInvocation.ts +126 -4
  332. package/src/runner/changeCapture.ts +342 -80
  333. package/src/runner/cli.ts +7 -6
  334. package/src/runner/postTaskCleanup.ts +55 -7
  335. package/src/runner/processSecurity.ts +433 -0
  336. package/src/runner/runTask.ts +441 -292
  337. package/src/runner/simpleProcess.ts +68 -33
  338. package/src/{taskProgress.ts → runner/taskProgress.ts} +3 -3
  339. package/src/{taskRuntime.ts → runner/taskRuntime.ts} +4 -3
  340. package/src/runner/taskStatusStore.ts +54 -0
  341. package/src/runner/watch.ts +290 -57
  342. package/src/security/contentRedaction.ts +31 -2
  343. package/src/security/discoveryTokenStore.ts +18 -4
  344. package/src/security/loopbackHost.ts +19 -0
  345. package/src/security/pathGuard.ts +35 -16
  346. package/src/security/planGuard.ts +2 -1
  347. package/src/security/runtimeGuard.ts +9 -6
  348. package/src/security/secretComparison.ts +13 -0
  349. package/src/security/sensitiveGuard.ts +27 -8
  350. package/src/security/toolInvocationGuard.ts +2 -2
  351. package/src/security/workspaceRootGuard.ts +53 -0
  352. package/src/smoke-test.ts +276 -273
  353. package/src/tools/catalog/index.ts +4 -0
  354. package/src/tools/{toolCatalog.ts → catalog/toolCatalog.ts} +3 -12
  355. package/src/tools/{toolRegistry.ts → catalog/toolRegistry.ts} +29 -16
  356. package/src/tools/definitions/toolDefs.ts +1264 -0
  357. package/src/tools/{auditSession.ts → diagnostics/auditSession.ts} +2 -2
  358. package/src/tools/{auditTask.ts → diagnostics/auditTask.ts} +196 -89
  359. package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +11 -11
  360. package/src/tools/diagnostics/index.ts +6 -0
  361. package/src/tools/{safeStatus.ts → diagnostics/safeStatus.ts} +7 -7
  362. package/src/tools/{safeViews.ts → diagnostics/safeViews.ts} +29 -19
  363. package/src/tools/{schemaDriftCheck.ts → diagnostics/schemaDriftCheck.ts} +3 -3
  364. package/src/tools/{createDirectSession.ts → direct/createDirectSession.ts} +9 -9
  365. package/src/tools/{finalizeDirectSession.ts → direct/finalizeDirectSession.ts} +11 -9
  366. package/src/tools/direct/index.ts +3 -0
  367. package/src/tools/{runDirectVerificationBundle.ts → direct/runDirectVerificationBundle.ts} +2 -2
  368. package/src/tools/{discoverTools.ts → discovery/discoverTools.ts} +6 -6
  369. package/src/tools/{explainTool.ts → discovery/explainTool.ts} +3 -3
  370. package/src/tools/discovery/index.ts +3 -0
  371. package/src/tools/{invokeDiscoveredTool.ts → discovery/invokeDiscoveredTool.ts} +7 -7
  372. package/src/tools/dispatch/coreDispatch.ts +379 -0
  373. package/src/tools/dispatch/diagnosticDispatch.ts +106 -0
  374. package/src/tools/dispatch/directDispatch.ts +168 -0
  375. package/src/tools/dispatch/goalDispatch.ts +128 -0
  376. package/src/tools/dispatch/releaseDispatch.ts +66 -0
  377. package/src/tools/dispatch/types.ts +24 -0
  378. package/src/tools/dispatch/validation.ts +78 -0
  379. package/src/tools/{getPlan.ts → goals/getPlan.ts} +4 -4
  380. package/src/tools/{goalSubgoalTask.ts → goals/goalSubgoalTask.ts} +43 -28
  381. package/src/tools/goals/index.ts +3 -0
  382. package/src/tools/{savePlan.ts → goals/savePlan.ts} +38 -8
  383. package/src/tools/registry.ts +54 -1858
  384. package/src/tools/{checkReleaseGate.ts → release/checkReleaseGate.ts} +3 -3
  385. package/src/tools/release/index.ts +2 -0
  386. package/src/tools/{releaseMode.ts → release/releaseMode.ts} +42 -24
  387. package/src/tools/tasks/cancelTask.ts +109 -0
  388. package/src/tools/{createTask.ts → tasks/createTask.ts} +56 -33
  389. package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +8 -8
  390. package/src/tools/tasks/evidencePack.ts +504 -0
  391. package/src/tools/{getTaskFile.ts → tasks/getTaskFile.ts} +4 -4
  392. package/src/tools/{getTaskStatus.ts → tasks/getTaskStatus.ts} +7 -5
  393. package/src/tools/{getTaskStdoutTail.ts → tasks/getTaskStdoutTail.ts} +13 -11
  394. package/src/tools/{getTaskSummary.ts → tasks/getTaskSummary.ts} +44 -33
  395. package/src/tools/tasks/index.ts +18 -0
  396. package/src/tools/{listTasks.ts → tasks/listTasks.ts} +4 -4
  397. package/src/tools/{reconcileTasks.ts → tasks/reconcileTasks.ts} +68 -66
  398. package/src/tools/tasks/retryTask.ts +69 -0
  399. package/src/tools/{runTaskLoop.ts → tasks/runTaskLoop.ts} +70 -35
  400. package/src/tools/{runVerification.ts → tasks/runVerification.ts} +6 -3
  401. package/src/tools/{taskLineage.ts → tasks/taskLineage.ts} +8 -7
  402. package/src/tools/{taskOutputs.ts → tasks/taskOutputs.ts} +27 -20
  403. package/src/tools/{androidDoctor.ts → workspace/androidDoctor.ts} +15 -10
  404. package/src/tools/{applyPatch.ts → workspace/applyPatch.ts} +38 -16
  405. package/src/tools/{discardWorktree.ts → workspace/discardWorktree.ts} +3 -3
  406. package/src/tools/workspace/index.ts +10 -0
  407. package/src/tools/{listAgents.ts → workspace/listAgents.ts} +11 -4
  408. package/src/tools/{listWorkspace.ts → workspace/listWorkspace.ts} +3 -3
  409. package/src/tools/{mergeWorktree.ts → workspace/mergeWorktree.ts} +3 -3
  410. package/src/tools/{readWorkspaceFile.ts → workspace/readWorkspaceFile.ts} +8 -8
  411. package/src/tools/{recommendAgentForTask.ts → workspace/recommendAgentForTask.ts} +3 -3
  412. package/src/tools/{searchWorkspace.ts → workspace/searchWorkspace.ts} +5 -5
  413. package/src/tools/workspace/syncFile.ts +292 -0
  414. package/src/utils/atomicFile.ts +58 -0
  415. package/src/utils/boundedFile.ts +116 -0
  416. package/src/utils/lockedJsonFile.ts +251 -0
  417. package/src/utils/platform.ts +31 -0
  418. package/src/utils/stableJson.ts +12 -0
  419. package/src/version.ts +3 -2
  420. package/src/watcherStatus.ts +19 -1
  421. package/ui/desktop-bootstrap.js +11 -0
  422. package/ui/desktop-bridge.js +36 -0
  423. package/ui/desktop.css +160 -0
  424. package/ui/getting-started.js +93 -0
  425. package/ui/i18n.js +484 -0
  426. package/ui/log-parser.js +30 -0
  427. package/ui/pages/audit.html +204 -3
  428. package/ui/pages/dashboard.html +937 -79
  429. package/ui/pages/direct-sessions.html +609 -60
  430. package/ui/pages/getting-started.html +43 -0
  431. package/ui/pages/logs.html +105 -22
  432. package/ui/pages/settings.html +101 -0
  433. package/ui/pages/task-detail.html +468 -438
  434. package/ui/pages/tasks.html +648 -65
  435. package/ui/pages/workspace.html +38 -1
  436. package/ui/settings.js +245 -0
  437. package/PatchWarden-Control.cmd +0 -6
  438. package/Restart-PatchWarden-Control.cmd +0 -6
  439. package/dist/test/unit/acceptance-engine.test.d.ts +0 -1
  440. package/dist/test/unit/acceptance-engine.test.js +0 -228
  441. package/dist/test/unit/agent-router.test.d.ts +0 -1
  442. package/dist/test/unit/agent-router.test.js +0 -287
  443. package/dist/test/unit/android-doctor.test.d.ts +0 -1
  444. package/dist/test/unit/android-doctor.test.js +0 -118
  445. package/dist/test/unit/audit-checks.test.d.ts +0 -1
  446. package/dist/test/unit/audit-checks.test.js +0 -350
  447. package/dist/test/unit/chinese-path.test.d.ts +0 -1
  448. package/dist/test/unit/chinese-path.test.js +0 -91
  449. package/dist/test/unit/command-guard.test.d.ts +0 -1
  450. package/dist/test/unit/command-guard.test.js +0 -160
  451. package/dist/test/unit/diagnose-task.test.d.ts +0 -1
  452. package/dist/test/unit/diagnose-task.test.js +0 -457
  453. package/dist/test/unit/direct-guards.test.d.ts +0 -1
  454. package/dist/test/unit/direct-guards.test.js +0 -213
  455. package/dist/test/unit/discovery-token-store.test.d.ts +0 -1
  456. package/dist/test/unit/discovery-token-store.test.js +0 -139
  457. package/dist/test/unit/evidence-pack.test.d.ts +0 -1
  458. package/dist/test/unit/evidence-pack.test.js +0 -130
  459. package/dist/test/unit/goal-graph.test.d.ts +0 -1
  460. package/dist/test/unit/goal-graph.test.js +0 -298
  461. package/dist/test/unit/goal-progress.test.d.ts +0 -1
  462. package/dist/test/unit/goal-progress.test.js +0 -381
  463. package/dist/test/unit/goal-status.test.d.ts +0 -1
  464. package/dist/test/unit/goal-status.test.js +0 -215
  465. package/dist/test/unit/goal-store.test.d.ts +0 -1
  466. package/dist/test/unit/goal-store.test.js +0 -253
  467. package/dist/test/unit/goal-subgoal-task.test.d.ts +0 -1
  468. package/dist/test/unit/goal-subgoal-task.test.js +0 -55
  469. package/dist/test/unit/goal-tools-registry.test.d.ts +0 -1
  470. package/dist/test/unit/goal-tools-registry.test.js +0 -190
  471. package/dist/test/unit/handoff-export.test.d.ts +0 -1
  472. package/dist/test/unit/handoff-export.test.js +0 -263
  473. package/dist/test/unit/invoke-discovered-tool.test.d.ts +0 -1
  474. package/dist/test/unit/invoke-discovered-tool.test.js +0 -167
  475. package/dist/test/unit/logging.test.d.ts +0 -1
  476. package/dist/test/unit/logging.test.js +0 -397
  477. package/dist/test/unit/path-guard.test.d.ts +0 -1
  478. package/dist/test/unit/path-guard.test.js +0 -109
  479. package/dist/test/unit/post-task-cleanup.test.d.ts +0 -1
  480. package/dist/test/unit/post-task-cleanup.test.js +0 -48
  481. package/dist/test/unit/project-policy-release-mode.test.d.ts +0 -1
  482. package/dist/test/unit/project-policy-release-mode.test.js +0 -125
  483. package/dist/test/unit/reconcile-tasks.test.d.ts +0 -1
  484. package/dist/test/unit/reconcile-tasks.test.js +0 -456
  485. package/dist/test/unit/release-gate.test.d.ts +0 -1
  486. package/dist/test/unit/release-gate.test.js +0 -242
  487. package/dist/test/unit/run-task-loop.test.d.ts +0 -1
  488. package/dist/test/unit/run-task-loop.test.js +0 -380
  489. package/dist/test/unit/safe-status.test.d.ts +0 -1
  490. package/dist/test/unit/safe-status.test.js +0 -165
  491. package/dist/test/unit/safe-views.test.d.ts +0 -1
  492. package/dist/test/unit/safe-views.test.js +0 -171
  493. package/dist/test/unit/schema-drift-check.test.d.ts +0 -1
  494. package/dist/test/unit/schema-drift-check.test.js +0 -176
  495. package/dist/test/unit/sensitive-guard.test.d.ts +0 -1
  496. package/dist/test/unit/sensitive-guard.test.js +0 -104
  497. package/dist/test/unit/subgoal-sync.test.d.ts +0 -1
  498. package/dist/test/unit/subgoal-sync.test.js +0 -183
  499. package/dist/test/unit/sync-file.test.d.ts +0 -1
  500. package/dist/test/unit/sync-file.test.js +0 -154
  501. package/dist/test/unit/tool-invocation-guard.test.d.ts +0 -1
  502. package/dist/test/unit/tool-invocation-guard.test.js +0 -432
  503. package/dist/test/unit/tool-usage-stats.test.d.ts +0 -1
  504. package/dist/test/unit/tool-usage-stats.test.js +0 -300
  505. package/dist/test/unit/toolSearch.test.d.ts +0 -1
  506. package/dist/test/unit/toolSearch.test.js +0 -571
  507. package/dist/test/unit/watcher-status.test.d.ts +0 -1
  508. package/dist/test/unit/watcher-status.test.js +0 -169
  509. package/dist/test/unit/worktree-manager.test.d.ts +0 -1
  510. package/dist/test/unit/worktree-manager.test.js +0 -176
  511. package/dist/tools/applyPatch.js +0 -41
  512. package/dist/tools/cancelTask.d.ts +0 -30
  513. package/dist/tools/cancelTask.js +0 -64
  514. package/dist/tools/evidencePack.js +0 -168
  515. package/dist/tools/finalizeDirectSession.js +0 -84
  516. package/dist/tools/goalSubgoalTask.js +0 -110
  517. package/dist/tools/killTask.d.ts +0 -15
  518. package/dist/tools/retryTask.js +0 -45
  519. package/dist/tools/runVerification.js +0 -36
  520. package/dist/tools/syncFile.js +0 -65
  521. package/docs/control-center/control-center-daily-driver.md +0 -211
  522. package/docs/control-center/control-center-mvp.md +0 -205
  523. package/docs/control-center/control-center-phase2.md +0 -159
  524. package/docs/release-v0.6.0.md +0 -71
  525. package/docs/release-v0.6.1.md +0 -75
  526. package/docs/release-v0.6.4.md +0 -45
  527. package/src/test/unit/acceptance-engine.test.ts +0 -261
  528. package/src/test/unit/agent-router.test.ts +0 -342
  529. package/src/test/unit/android-doctor.test.ts +0 -158
  530. package/src/test/unit/audit-checks.test.ts +0 -567
  531. package/src/test/unit/chinese-path.test.ts +0 -106
  532. package/src/test/unit/command-guard.test.ts +0 -221
  533. package/src/test/unit/diagnose-task.test.ts +0 -544
  534. package/src/test/unit/direct-guards.test.ts +0 -297
  535. package/src/test/unit/discovery-token-store.test.ts +0 -181
  536. package/src/test/unit/evidence-pack.test.ts +0 -142
  537. package/src/test/unit/goal-graph.test.ts +0 -347
  538. package/src/test/unit/goal-progress.test.ts +0 -538
  539. package/src/test/unit/goal-status.test.ts +0 -270
  540. package/src/test/unit/goal-store.test.ts +0 -318
  541. package/src/test/unit/goal-subgoal-task.test.ts +0 -72
  542. package/src/test/unit/goal-tools-registry.test.ts +0 -243
  543. package/src/test/unit/handoff-export.test.ts +0 -295
  544. package/src/test/unit/invoke-discovered-tool.test.ts +0 -216
  545. package/src/test/unit/logging.test.ts +0 -497
  546. package/src/test/unit/path-guard.test.ts +0 -150
  547. package/src/test/unit/post-task-cleanup.test.ts +0 -53
  548. package/src/test/unit/project-policy-release-mode.test.ts +0 -156
  549. package/src/test/unit/reconcile-tasks.test.ts +0 -551
  550. package/src/test/unit/release-gate.test.ts +0 -314
  551. package/src/test/unit/run-task-loop.test.ts +0 -425
  552. package/src/test/unit/safe-status.test.ts +0 -187
  553. package/src/test/unit/safe-views.test.ts +0 -184
  554. package/src/test/unit/schema-drift-check.test.ts +0 -260
  555. package/src/test/unit/sensitive-guard.test.ts +0 -124
  556. package/src/test/unit/subgoal-sync.test.ts +0 -236
  557. package/src/test/unit/sync-file.test.ts +0 -231
  558. package/src/test/unit/tool-invocation-guard.test.ts +0 -542
  559. package/src/test/unit/tool-usage-stats.test.ts +0 -384
  560. package/src/test/unit/toolSearch.test.ts +0 -756
  561. package/src/test/unit/watcher-status.test.ts +0 -190
  562. package/src/test/unit/worktree-manager.test.ts +0 -247
  563. package/src/tools/cancelTask.ts +0 -70
  564. package/src/tools/evidencePack.ts +0 -205
  565. package/src/tools/retryTask.ts +0 -53
  566. package/src/tools/syncFile.ts +0 -122
  567. /package/dist/tools/{toolCatalog.d.ts → catalog/toolCatalog.d.ts} +0 -0
  568. /package/dist/tools/{toolSearch.d.ts → catalog/toolSearch.d.ts} +0 -0
  569. /package/dist/tools/{toolSearch.js → catalog/toolSearch.js} +0 -0
  570. /package/dist/tools/{toolUsageStats.d.ts → catalog/toolUsageStats.d.ts} +0 -0
  571. /package/dist/tools/{toolUsageStats.js → catalog/toolUsageStats.js} +0 -0
  572. /package/dist/tools/{runDirectVerificationBundle.d.ts → direct/runDirectVerificationBundle.d.ts} +0 -0
  573. /package/dist/tools/{getPlan.d.ts → goals/getPlan.d.ts} +0 -0
  574. /package/dist/tools/{savePlan.d.ts → goals/savePlan.d.ts} +0 -0
  575. /package/dist/tools/{getTaskFile.d.ts → tasks/getTaskFile.d.ts} +0 -0
  576. /package/dist/tools/{getTaskProgress.d.ts → tasks/getTaskProgress.d.ts} +0 -0
  577. /package/dist/tools/{getTaskProgress.js → tasks/getTaskProgress.js} +0 -0
  578. /package/dist/tools/{getTaskStdoutTail.d.ts → tasks/getTaskStdoutTail.d.ts} +0 -0
  579. /package/dist/tools/{getTaskSummary.d.ts → tasks/getTaskSummary.d.ts} +0 -0
  580. /package/dist/tools/{killTask.js → tasks/killTask.js} +0 -0
  581. /package/dist/tools/{runVerification.d.ts → tasks/runVerification.d.ts} +0 -0
  582. /package/dist/tools/{taskLineage.d.ts → tasks/taskLineage.d.ts} +0 -0
  583. /package/dist/tools/{taskOutputs.d.ts → tasks/taskOutputs.d.ts} +0 -0
  584. /package/dist/tools/{waitForTask.d.ts → tasks/waitForTask.d.ts} +0 -0
  585. /package/dist/tools/{waitForTask.js → tasks/waitForTask.js} +0 -0
  586. /package/dist/tools/{androidDoctor.d.ts → workspace/androidDoctor.d.ts} +0 -0
  587. /package/dist/tools/{discardWorktree.d.ts → workspace/discardWorktree.d.ts} +0 -0
  588. /package/dist/tools/{listWorkspace.d.ts → workspace/listWorkspace.d.ts} +0 -0
  589. /package/dist/tools/{mergeWorktree.d.ts → workspace/mergeWorktree.d.ts} +0 -0
  590. /package/dist/tools/{readWorkspaceFile.d.ts → workspace/readWorkspaceFile.d.ts} +0 -0
  591. /package/dist/tools/{recommendAgentForTask.d.ts → workspace/recommendAgentForTask.d.ts} +0 -0
  592. /package/dist/tools/{searchWorkspace.d.ts → workspace/searchWorkspace.d.ts} +0 -0
  593. /package/src/tools/{toolSearch.ts → catalog/toolSearch.ts} +0 -0
  594. /package/src/tools/{toolUsageStats.ts → catalog/toolUsageStats.ts} +0 -0
  595. /package/src/tools/{getTaskProgress.ts → tasks/getTaskProgress.ts} +0 -0
  596. /package/src/tools/{killTask.ts → tasks/killTask.ts} +0 -0
  597. /package/src/tools/{waitForTask.ts → tasks/waitForTask.ts} +0 -0
@@ -0,0 +1,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 —
@@ -1,8 +1,8 @@
1
- export declare function retryTask(taskId: string): {
1
+ export declare function retryTask(taskId: string): Promise<{
2
2
  original_task_id: string;
3
3
  new_task_id: string;
4
4
  plan_id: string;
5
5
  agent: string;
6
6
  status: import("./createTask.js").TaskStatus;
7
7
  message: string;
8
- };
8
+ }>;
@@ -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;
@@ -1,13 +1,13 @@
1
1
  import { setTimeout as sleep } from "node:timers/promises";
2
- import { getConfig } from "../config.js";
3
- import { createWorktree } from "../goal/worktreeManager.js";
4
- import { guardWorkspacePath } from "../security/pathGuard.js";
5
- import { createDirectSession } from "./createDirectSession.js";
2
+ import { getConfig } from "../../config.js";
3
+ import { createWorktree } from "../../goal/worktreeManager.js";
4
+ import { guardWorkspacePath } from "../../security/pathGuard.js";
5
+ import { createDirectSession } from "../direct/createDirectSession.js";
6
6
  import { createTask } from "./createTask.js";
7
- import { recommendAgentForTask } from "./recommendAgentForTask.js";
8
- import { runDirectVerificationBundle } from "./runDirectVerificationBundle.js";
7
+ import { recommendAgentForTask } from "../workspace/recommendAgentForTask.js";
8
+ import { runDirectVerificationBundle } from "../direct/runDirectVerificationBundle.js";
9
9
  import { waitForTask } from "./waitForTask.js";
10
- import { safeAudit, safeAuditDirectSession, safeFinalizeDirectSession, safeResult, safeTestSummary } from "./safeViews.js";
10
+ import { safeAudit, safeAuditDirectSession, safeFinalizeDirectSession, safeResult, safeTestSummary } from "../diagnostics/safeViews.js";
11
11
  import { createLineageId, writeTaskLineage, } from "./taskLineage.js";
12
12
  const DEFAULT_DEPS = {
13
13
  createTask,
@@ -130,17 +130,17 @@ export async function runTaskLoopWithDeps(input, deps) {
130
130
  timeout_seconds: normalized.task_timeout_seconds,
131
131
  execution_mode: "assess_only",
132
132
  };
133
- const assessment = deps.createTask(assessmentInput);
133
+ const assessment = asRecord(await deps.createTask(assessmentInput));
134
134
  if (assessment.decision === "blocked") {
135
- return finalize("blocked", "high_risk_blocked", "Risk assessment blocked task execution.", assessment.reason_codes?.join(", "));
135
+ return finalize("blocked", "high_risk_blocked", "Risk assessment blocked task execution.", asArray(assessment.reason_codes).map(String).join(", "));
136
136
  }
137
137
  if (assessment.decision === "needs_confirm") {
138
138
  return finalize("blocked", "user_confirmation_required", "Ask the user to confirm the assessment before executing the loop.");
139
139
  }
140
- const created = deps.createTask({
140
+ const created = asRecord(await deps.createTask({
141
141
  execution_mode: "execute",
142
142
  assessment_id: String(assessment.assessment_id || ""),
143
- });
143
+ }));
144
144
  const taskId = String(created.task_id || "");
145
145
  if (!taskId) {
146
146
  return finalize("failed", "policy_blocked", "create_task returned no task_id.", "create_task returned no task_id");
@@ -153,9 +153,9 @@ export async function runTaskLoopWithDeps(input, deps) {
153
153
  if (wait.stop_reason) {
154
154
  return finalize("blocked", wait.stop_reason, wait.next_action, wait.error);
155
155
  }
156
- const result = deps.safeResult(taskId, { max_items: 8 });
157
- const tests = deps.safeTestSummary(taskId);
158
- const audit = deps.safeAudit(taskId, { max_items: 8 });
156
+ const result = asRecord(deps.safeResult(taskId, { max_items: 8 }));
157
+ const tests = asRecord(deps.safeTestSummary(taskId));
158
+ const audit = asRecord(deps.safeAudit(taskId, { max_items: 8 }));
159
159
  const round = buildRound(iteration, taskId, role, result, tests, audit);
160
160
  lineage.rounds.push(round);
161
161
  lineage.updated_at = deps.now().toISOString();
@@ -204,20 +204,32 @@ async function waitUntilTerminal(taskId, timeoutSeconds, deps) {
204
204
  error: `Task loop timed out waiting for ${taskId}.`,
205
205
  };
206
206
  }
207
- function buildRound(iteration, taskId, role, result, tests, audit) {
208
- const failChecks = Array.isArray(audit.fail_checks) ? audit.fail_checks.map((entry) => String(entry.name || entry)) : [];
209
- const warnChecks = Array.isArray(audit.warn_checks) ? audit.warn_checks.map((entry) => String(entry.name || entry)) : [];
207
+ function buildRound(iteration, taskId, role, resultValue, testsValue, auditValue) {
208
+ const result = asRecord(resultValue);
209
+ const tests = asRecord(testsValue);
210
+ const audit = asRecord(auditValue);
211
+ const resultVerification = asRecord(result.verification);
212
+ const auditAcceptance = asRecord(audit.acceptance);
213
+ const failChecks = asArray(audit.fail_checks).map((entry) => {
214
+ const check = asRecord(entry);
215
+ return String(check.name || entry);
216
+ });
217
+ const warnChecks = asArray(audit.warn_checks).map((entry) => {
218
+ const check = asRecord(entry);
219
+ return String(check.name || entry);
220
+ });
221
+ const recommendedActions = asArray(audit.recommended_next_actions);
210
222
  return {
211
223
  iteration,
212
224
  task_id: taskId,
213
225
  role,
214
226
  status: String(result.status || "unknown"),
215
227
  terminal: Boolean(result.terminal),
216
- verification_status: String(tests.status || result.verification?.status || "not_available"),
217
- audit_verdict: String(audit.verdict || audit.acceptance?.verdict || "unknown"),
228
+ verification_status: String(tests.status || resultVerification.status || "not_available"),
229
+ audit_verdict: String(audit.verdict || auditAcceptance.verdict || "unknown"),
218
230
  fail_checks: failChecks,
219
231
  warn_checks: warnChecks,
220
- next_action: String(result.next_action || audit.recommended_next_actions?.[0] || "review_task"),
232
+ next_action: String(result.next_action || recommendedActions[0] || "review_task"),
221
233
  };
222
234
  }
223
235
  function isSuccessfulRound(round) {
@@ -228,23 +240,27 @@ function isSuccessfulRound(round) {
228
240
  round.warn_checks.length === 0 &&
229
241
  round.audit_verdict === "pass");
230
242
  }
231
- function isHardStop(round, result, audit, stopOnHighRisk) {
243
+ function isHardStop(round, resultValue, auditValue, stopOnHighRisk) {
232
244
  if (["failed_scope_violation", "failed_policy_violation", "canceled"].includes(round.status))
233
245
  return true;
234
246
  if (!stopOnHighRisk)
235
247
  return false;
236
248
  const checkNames = [...round.fail_checks, ...round.warn_checks].join(" ").toLowerCase();
237
- const reason = String(result.failure_reason || audit.acceptance?.reason || "").toLowerCase();
249
+ const result = asRecord(resultValue);
250
+ const audit = asRecord(auditValue);
251
+ const reason = String(result.failure_reason || asRecord(audit.acceptance).reason || "").toLowerCase();
238
252
  return /scope|secret|sensitive|publish|release|policy|push/.test(`${checkNames} ${reason}`);
239
253
  }
240
- function hardStopReason(round, result) {
254
+ function hardStopReason(round, resultValue) {
255
+ const result = asRecord(resultValue);
241
256
  if (round.status === "failed_scope_violation" || round.status === "failed_policy_violation")
242
257
  return "policy_blocked";
243
258
  if (String(result.failure_reason || "").toLowerCase().includes("timeout"))
244
259
  return "agent_timeout";
245
260
  return "high_risk_blocked";
246
261
  }
247
- function buildFixGoal(originalGoal, result, round) {
262
+ function buildFixGoal(originalGoal, resultValue, round) {
263
+ const result = asRecord(resultValue);
248
264
  const failedCommand = result.failed_command ? ` Failed command: ${result.failed_command}.` : "";
249
265
  return [
250
266
  `Fix the failing verification for this PatchWarden loop without changing unrelated behavior.`,
@@ -293,7 +309,7 @@ async function runDirectVerification(lineageId, normalized, repoPath, deps) {
293
309
  }
294
310
  let sessionId = "";
295
311
  try {
296
- const session = deps.createDirectSession({
312
+ const session = await deps.createDirectSession({
297
313
  repo_path: repoPath,
298
314
  title: `Direct verification for ${lineageId}`,
299
315
  });
@@ -303,8 +319,12 @@ async function runDirectVerification(lineageId, normalized, repoPath, deps) {
303
319
  commands: normalized.direct_verify_commands,
304
320
  timeout_seconds: normalized.direct_verify_timeout_seconds,
305
321
  });
306
- const finalized = deps.safeFinalizeDirectSession(sessionId, { max_items: 8 });
307
- const audit = deps.safeAuditDirectSession(sessionId, { max_items: 8 });
322
+ const finalized = asRecord(await deps.safeFinalizeDirectSession(sessionId, { max_items: 8 }));
323
+ const audit = asRecord(deps.safeAuditDirectSession(sessionId, { max_items: 8 }));
324
+ const auditEvidence = asRecord(audit.evidence);
325
+ const auditDecision = audit.decision === "pass" || audit.decision === "warn" || audit.decision === "fail"
326
+ ? audit.decision
327
+ : "not_run";
308
328
  const evidence = {
309
329
  session_id: sessionId,
310
330
  status: bundle.status,
@@ -312,8 +332,8 @@ async function runDirectVerification(lineageId, normalized, repoPath, deps) {
312
332
  passed_commands: bundle.passed_commands,
313
333
  failed_commands: bundle.failed_commands,
314
334
  timed_out_commands: bundle.timed_out_commands,
315
- audit_decision: audit.decision || "not_run",
316
- changed_files_total: Number(finalized.changed_files_total || audit.evidence?.changed_files_total || 0),
335
+ audit_decision: auditDecision,
336
+ changed_files_total: Number(finalized.changed_files_total || auditEvidence.changed_files_total || 0),
317
337
  next_action: String(audit.next_action || bundle.next_action || "review_direct_session"),
318
338
  };
319
339
  if (bundle.status !== "passed") {
@@ -325,7 +345,7 @@ async function runDirectVerification(lineageId, normalized, repoPath, deps) {
325
345
  error: "Direct verification failed.",
326
346
  };
327
347
  }
328
- if (audit.decision === "fail") {
348
+ if (auditDecision === "fail") {
329
349
  return {
330
350
  evidence,
331
351
  stop_reason: "direct_audit_failed",
@@ -338,7 +358,7 @@ async function runDirectVerification(lineageId, normalized, repoPath, deps) {
338
358
  evidence,
339
359
  final_status: "accepted",
340
360
  next_action: "accept",
341
- warning: audit.decision === "warn" ? "Direct audit completed with warnings." : undefined,
361
+ warning: auditDecision === "warn" ? "Direct audit completed with warnings." : undefined,
342
362
  };
343
363
  }
344
364
  catch (err) {
@@ -357,6 +377,14 @@ async function runDirectVerification(lineageId, normalized, repoPath, deps) {
357
377
  };
358
378
  }
359
379
  }
380
+ function asRecord(value) {
381
+ return value && typeof value === "object" && !Array.isArray(value)
382
+ ? value
383
+ : {};
384
+ }
385
+ function asArray(value) {
386
+ return Array.isArray(value) ? value : [];
387
+ }
360
388
  function normalizeInput(input) {
361
389
  const config = getConfig();
362
390
  const repoPath = String(input.repo_path || "").trim();
@@ -0,0 +1,38 @@
1
+ import { readDirectSession, appendDirectSessionVerificationRun, withDirectSessionMutationLockAsync, } from "../../direct/directSessionStore.js";
2
+ import { guardDirectSessionActive } from "../../direct/directGuards.js";
3
+ import { runDirectVerification } from "../../direct/directVerification.js";
4
+ export async function runVerification(input) {
5
+ return withDirectSessionMutationLockAsync(input.session_id, async () => {
6
+ // 1. Read session and guard active
7
+ const session = readDirectSession(input.session_id);
8
+ guardDirectSessionActive(session);
9
+ // 2. Call runDirectVerification with command, resolvedRepoPath,
10
+ // sessionId, and timeoutSeconds
11
+ const timeoutSeconds = input.timeout_seconds ?? 120;
12
+ const result = await runDirectVerification({
13
+ command: input.command,
14
+ resolvedRepoPath: session.resolved_repo_path,
15
+ sessionId: input.session_id,
16
+ timeoutSeconds,
17
+ });
18
+ // 3. Append verification run to session
19
+ appendDirectSessionVerificationRun(input.session_id, result.run);
20
+ // 4. Return result
21
+ return {
22
+ command: result.run.command,
23
+ exit_code: result.run.exit_code,
24
+ passed: result.run.passed,
25
+ timed_out: result.run.timed_out,
26
+ redacted: Boolean(result.run.redacted),
27
+ redaction_categories: result.run.redaction_categories || [],
28
+ started_at: result.run.started_at,
29
+ finished_at: result.run.finished_at,
30
+ stdout_tail: result.run.stdout_tail,
31
+ stderr_tail: result.run.stderr_tail,
32
+ log_path: result.run.log_path,
33
+ next_action: result.run.passed
34
+ ? "Call finalize_direct_session to complete the session."
35
+ : "Review the verification output and apply_patch to fix issues.",
36
+ };
37
+ });
38
+ }
@@ -1,10 +1,11 @@
1
1
  import { randomBytes } from "node:crypto";
2
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { existsSync, mkdirSync, readFileSync } from "node:fs";
3
3
  import { join, resolve } from "node:path";
4
- import { getConfig } from "../config.js";
5
- import { guardReadPath } from "../security/pathGuard.js";
6
- import { redactSensitiveValue } from "../security/contentRedaction.js";
7
- import { PatchWardenError } from "../errors.js";
4
+ import { getConfig } from "../../config.js";
5
+ import { guardReadPath } from "../../security/pathGuard.js";
6
+ import { redactSensitiveValue } from "../../security/contentRedaction.js";
7
+ import { PatchWardenError } from "../../errors.js";
8
+ import { atomicWriteFileSync, atomicWriteJsonFileSync } from "../../utils/atomicFile.js";
8
9
  export function createLineageId(now = new Date()) {
9
10
  const stamp = now.toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
10
11
  return `lineage_${stamp}_${randomBytes(4).toString("hex")}`;
@@ -14,8 +15,8 @@ export function writeTaskLineage(record) {
14
15
  const lineageDir = resolve(config.workspaceRoot, ".patchwarden", "lineages", record.lineage_id);
15
16
  mkdirSync(lineageDir, { recursive: true });
16
17
  const safeRecord = redactSensitiveValue(record).value;
17
- writeFileSync(join(lineageDir, "lineage.json"), JSON.stringify(safeRecord, null, 2) + "\n", "utf-8");
18
- writeFileSync(join(lineageDir, "SUMMARY.md"), buildSummaryMarkdown(safeRecord), "utf-8");
18
+ atomicWriteJsonFileSync(join(lineageDir, "lineage.json"), safeRecord);
19
+ atomicWriteFileSync(join(lineageDir, "SUMMARY.md"), buildSummaryMarkdown(safeRecord));
19
20
  return toSafeTaskLineage(safeRecord);
20
21
  }
21
22
  export function getTaskLineage(lineageId, options = {}) {