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,14 +1,15 @@
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, type CreateTaskInput } 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";
11
- import type { TaskTemplateName } from "./taskTemplates.js";
10
+ import { safeAudit, safeAuditDirectSession, safeFinalizeDirectSession, safeResult, safeTestSummary } from "../diagnostics/safeViews.js";
11
+ import type { TaskTemplateName } from "../taskTemplates.js";
12
+ import { isTerminalTaskStatus } from "./taskStates.js";
12
13
  import {
13
14
  createLineageId,
14
15
  writeTaskLineage,
@@ -86,16 +87,6 @@ const DEFAULT_DEPS: RunTaskLoopDeps = {
86
87
  sleep,
87
88
  };
88
89
 
89
- const TERMINAL_STATUSES = new Set([
90
- "done",
91
- "done_by_agent",
92
- "failed",
93
- "failed_verification",
94
- "failed_scope_violation",
95
- "failed_policy_violation",
96
- "canceled",
97
- ]);
98
-
99
90
  export async function runTaskLoop(input: RunTaskLoopInput): Promise<RunTaskLoopOutput> {
100
91
  return runTaskLoopWithDeps(input, DEFAULT_DEPS);
101
92
  }
@@ -202,18 +193,23 @@ export async function runTaskLoopWithDeps(
202
193
  timeout_seconds: normalized.task_timeout_seconds,
203
194
  execution_mode: "assess_only",
204
195
  };
205
- const assessment = await deps.createTask(assessmentInput as CreateTaskInput & { execution_mode: "assess_only" }) as any;
196
+ const assessment = asRecord(await deps.createTask(assessmentInput));
206
197
  if (assessment.decision === "blocked") {
207
- return finalize("blocked", "high_risk_blocked", "Risk assessment blocked task execution.", assessment.reason_codes?.join(", "));
198
+ return finalize(
199
+ "blocked",
200
+ "high_risk_blocked",
201
+ "Risk assessment blocked task execution.",
202
+ asArray(assessment.reason_codes).map(String).join(", "),
203
+ );
208
204
  }
209
205
  if (assessment.decision === "needs_confirm") {
210
206
  return finalize("blocked", "user_confirmation_required", "Ask the user to confirm the assessment before executing the loop.");
211
207
  }
212
208
 
213
- const created = await deps.createTask({
209
+ const created = asRecord(await deps.createTask({
214
210
  execution_mode: "execute",
215
211
  assessment_id: String(assessment.assessment_id || ""),
216
- }) as any;
212
+ }));
217
213
  const taskId = String(created.task_id || "");
218
214
  if (!taskId) {
219
215
  return finalize("failed", "policy_blocked", "create_task returned no task_id.", "create_task returned no task_id");
@@ -226,9 +222,9 @@ export async function runTaskLoopWithDeps(
226
222
  return finalize("blocked", wait.stop_reason, wait.next_action, wait.error);
227
223
  }
228
224
 
229
- const result = deps.safeResult(taskId, { max_items: 8 }) as any;
230
- const tests = deps.safeTestSummary(taskId) as any;
231
- const audit = deps.safeAudit(taskId, { max_items: 8 }) as any;
225
+ const result = asRecord(deps.safeResult(taskId, { max_items: 8 }));
226
+ const tests = asRecord(deps.safeTestSummary(taskId));
227
+ const audit = asRecord(deps.safeAudit(taskId, { max_items: 8 }));
232
228
  const round = buildRound(iteration, taskId, role, result, tests, audit);
233
229
  lineage.rounds.push(round);
234
230
  lineage.updated_at = deps.now().toISOString();
@@ -267,8 +263,8 @@ async function waitUntilTerminal(
267
263
  ): Promise<{ stop_reason?: TaskLoopStopReason; next_action: string; error?: string }> {
268
264
  const deadline = Date.now() + timeoutSeconds * 1000;
269
265
  while (Date.now() < deadline) {
270
- const waited = await deps.waitForTask(taskId, 30) as any;
271
- if (waited.terminal || TERMINAL_STATUSES.has(String(waited.status))) {
266
+ const waited = await deps.waitForTask(taskId, 30);
267
+ if (waited.terminal || isTerminalTaskStatus(String(waited.status))) {
272
268
  return { next_action: waited.next_action || "safe_audit" };
273
269
  }
274
270
  if (waited.next_tool_call?.name === "health_check" || waited.continuation_required === false) {
@@ -291,23 +287,35 @@ function buildRound(
291
287
  iteration: number,
292
288
  taskId: string,
293
289
  role: TaskLineageRound["role"],
294
- result: any,
295
- tests: any,
296
- audit: any
290
+ resultValue: unknown,
291
+ testsValue: unknown,
292
+ auditValue: unknown,
297
293
  ): TaskLineageRound {
298
- const failChecks = Array.isArray(audit.fail_checks) ? audit.fail_checks.map((entry: any) => String(entry.name || entry)) : [];
299
- const warnChecks = Array.isArray(audit.warn_checks) ? audit.warn_checks.map((entry: any) => String(entry.name || entry)) : [];
294
+ const result = asRecord(resultValue);
295
+ const tests = asRecord(testsValue);
296
+ const audit = asRecord(auditValue);
297
+ const resultVerification = asRecord(result.verification);
298
+ const auditAcceptance = asRecord(audit.acceptance);
299
+ const failChecks = asArray(audit.fail_checks).map((entry) => {
300
+ const check = asRecord(entry);
301
+ return String(check.name || entry);
302
+ });
303
+ const warnChecks = asArray(audit.warn_checks).map((entry) => {
304
+ const check = asRecord(entry);
305
+ return String(check.name || entry);
306
+ });
307
+ const recommendedActions = asArray(audit.recommended_next_actions);
300
308
  return {
301
309
  iteration,
302
310
  task_id: taskId,
303
311
  role,
304
312
  status: String(result.status || "unknown"),
305
313
  terminal: Boolean(result.terminal),
306
- verification_status: String(tests.status || result.verification?.status || "not_available"),
307
- audit_verdict: String(audit.verdict || audit.acceptance?.verdict || "unknown"),
314
+ verification_status: String(tests.status || resultVerification.status || "not_available"),
315
+ audit_verdict: String(audit.verdict || auditAcceptance.verdict || "unknown"),
308
316
  fail_checks: failChecks,
309
317
  warn_checks: warnChecks,
310
- next_action: String(result.next_action || audit.recommended_next_actions?.[0] || "review_task"),
318
+ next_action: String(result.next_action || recommendedActions[0] || "review_task"),
311
319
  };
312
320
  }
313
321
 
@@ -322,21 +330,26 @@ function isSuccessfulRound(round: TaskLineageRound): boolean {
322
330
  );
323
331
  }
324
332
 
325
- function isHardStop(round: TaskLineageRound, result: any, audit: any, stopOnHighRisk: boolean): boolean {
326
- if (["failed_scope_violation", "failed_policy_violation", "canceled"].includes(round.status)) return true;
333
+ function isHardStop(round: TaskLineageRound, resultValue: unknown, auditValue: unknown, stopOnHighRisk: boolean): boolean {
334
+ if (["failed_scope_violation", "failed_policy_violation", "canceled", "timeout"].includes(round.status)) return true;
327
335
  if (!stopOnHighRisk) return false;
328
336
  const checkNames = [...round.fail_checks, ...round.warn_checks].join(" ").toLowerCase();
329
- const reason = String(result.failure_reason || audit.acceptance?.reason || "").toLowerCase();
337
+ const result = asRecord(resultValue);
338
+ const audit = asRecord(auditValue);
339
+ const reason = String(result.failure_reason || asRecord(audit.acceptance).reason || "").toLowerCase();
330
340
  return /scope|secret|sensitive|publish|release|policy|push/.test(`${checkNames} ${reason}`);
331
341
  }
332
342
 
333
- function hardStopReason(round: TaskLineageRound, result: any): TaskLoopStopReason {
343
+ function hardStopReason(round: TaskLineageRound, resultValue: unknown): TaskLoopStopReason {
344
+ const result = asRecord(resultValue);
334
345
  if (round.status === "failed_scope_violation" || round.status === "failed_policy_violation") return "policy_blocked";
346
+ if (round.status === "timeout") return "agent_timeout";
335
347
  if (String(result.failure_reason || "").toLowerCase().includes("timeout")) return "agent_timeout";
336
348
  return "high_risk_blocked";
337
349
  }
338
350
 
339
- function buildFixGoal(originalGoal: string, result: any, round: TaskLineageRound): string {
351
+ function buildFixGoal(originalGoal: string, resultValue: unknown, round: TaskLineageRound): string {
352
+ const result = asRecord(resultValue);
340
353
  const failedCommand = result.failed_command ? ` Failed command: ${result.failed_command}.` : "";
341
354
  return [
342
355
  `Fix the failing verification for this PatchWarden loop without changing unrelated behavior.`,
@@ -413,8 +426,12 @@ async function runDirectVerification(
413
426
  commands: normalized.direct_verify_commands,
414
427
  timeout_seconds: normalized.direct_verify_timeout_seconds,
415
428
  });
416
- const finalized = await deps.safeFinalizeDirectSession(sessionId, { max_items: 8 }) as any;
417
- const audit = deps.safeAuditDirectSession(sessionId, { max_items: 8 }) as any;
429
+ const finalized = asRecord(await deps.safeFinalizeDirectSession(sessionId, { max_items: 8 }));
430
+ const audit = asRecord(deps.safeAuditDirectSession(sessionId, { max_items: 8 }));
431
+ const auditEvidence = asRecord(audit.evidence);
432
+ const auditDecision = audit.decision === "pass" || audit.decision === "warn" || audit.decision === "fail"
433
+ ? audit.decision
434
+ : "not_run";
418
435
  const evidence: TaskLineageDirectSession = {
419
436
  session_id: sessionId,
420
437
  status: bundle.status,
@@ -422,8 +439,8 @@ async function runDirectVerification(
422
439
  passed_commands: bundle.passed_commands,
423
440
  failed_commands: bundle.failed_commands,
424
441
  timed_out_commands: bundle.timed_out_commands,
425
- audit_decision: audit.decision || "not_run",
426
- changed_files_total: Number(finalized.changed_files_total || audit.evidence?.changed_files_total || 0),
442
+ audit_decision: auditDecision,
443
+ changed_files_total: Number(finalized.changed_files_total || auditEvidence.changed_files_total || 0),
427
444
  next_action: String(audit.next_action || bundle.next_action || "review_direct_session"),
428
445
  };
429
446
  if (bundle.status !== "passed") {
@@ -435,7 +452,7 @@ async function runDirectVerification(
435
452
  error: "Direct verification failed.",
436
453
  };
437
454
  }
438
- if (audit.decision === "fail") {
455
+ if (auditDecision === "fail") {
439
456
  return {
440
457
  evidence,
441
458
  stop_reason: "direct_audit_failed",
@@ -448,7 +465,7 @@ async function runDirectVerification(
448
465
  evidence,
449
466
  final_status: "accepted",
450
467
  next_action: "accept",
451
- warning: audit.decision === "warn" ? "Direct audit completed with warnings." : undefined,
468
+ warning: auditDecision === "warn" ? "Direct audit completed with warnings." : undefined,
452
469
  };
453
470
  } catch (err) {
454
471
  const message = err instanceof Error ? err.message : String(err);
@@ -467,6 +484,16 @@ async function runDirectVerification(
467
484
  }
468
485
  }
469
486
 
487
+ function asRecord(value: unknown): Record<string, unknown> {
488
+ return value && typeof value === "object" && !Array.isArray(value)
489
+ ? value as Record<string, unknown>
490
+ : {};
491
+ }
492
+
493
+ function asArray(value: unknown): unknown[] {
494
+ return Array.isArray(value) ? value : [];
495
+ }
496
+
470
497
  function normalizeInput(input: RunTaskLoopInput): Required<Omit<RunTaskLoopInput, "agent" | "scope_files" | "worktree_base_branch">> & { agent?: string; scope_files?: string[]; worktree_base_branch?: string } {
471
498
  const config = getConfig();
472
499
  const repoPath = String(input.repo_path || "").trim();
@@ -1,9 +1,10 @@
1
1
  import {
2
2
  readDirectSession,
3
3
  appendDirectSessionVerificationRun,
4
- } from "../direct/directSessionStore.js";
5
- import { guardDirectSessionActive } from "../direct/directGuards.js";
6
- import { runDirectVerification } from "../direct/directVerification.js";
4
+ withDirectSessionMutationLockAsync,
5
+ } from "../../direct/directSessionStore.js";
6
+ import { guardDirectSessionActive } from "../../direct/directGuards.js";
7
+ import { runDirectVerification } from "../../direct/directVerification.js";
7
8
 
8
9
  export interface RunVerificationInput {
9
10
  session_id: string;
@@ -29,6 +30,7 @@ export interface RunVerificationOutput {
29
30
  export async function runVerification(
30
31
  input: RunVerificationInput
31
32
  ): Promise<RunVerificationOutput> {
33
+ return withDirectSessionMutationLockAsync(input.session_id, async () => {
32
34
  // 1. Read session and guard active
33
35
  const session = readDirectSession(input.session_id);
34
36
  guardDirectSessionActive(session);
@@ -63,4 +65,5 @@ export async function runVerification(
63
65
  ? "Call finalize_direct_session to complete the session."
64
66
  : "Review the verification output and apply_patch to fix issues.",
65
67
  };
68
+ });
66
69
  }
@@ -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
 
9
10
  export type TaskLoopStopReason =
10
11
  | "success"
@@ -119,8 +120,8 @@ export function writeTaskLineage(record: TaskLineageRecord): SafeTaskLineage {
119
120
  const lineageDir = resolve(config.workspaceRoot, ".patchwarden", "lineages", record.lineage_id);
120
121
  mkdirSync(lineageDir, { recursive: true });
121
122
  const safeRecord = redactSensitiveValue(record).value as TaskLineageRecord;
122
- writeFileSync(join(lineageDir, "lineage.json"), JSON.stringify(safeRecord, null, 2) + "\n", "utf-8");
123
- writeFileSync(join(lineageDir, "SUMMARY.md"), buildSummaryMarkdown(safeRecord), "utf-8");
123
+ atomicWriteJsonFileSync(join(lineageDir, "lineage.json"), safeRecord);
124
+ atomicWriteFileSync(join(lineageDir, "SUMMARY.md"), buildSummaryMarkdown(safeRecord));
124
125
  return toSafeTaskLineage(safeRecord);
125
126
  }
126
127
 
@@ -1,9 +1,13 @@
1
1
  import { getTaskFile, GetTaskFileOutput } from "./getTaskFile.js";
2
- import { existsSync, readFileSync, statSync } from "node:fs";
2
+ import { existsSync, statSync } from "node:fs";
3
3
  import { join, resolve } from "node:path";
4
- import { getConfig, getTasksDir } from "../config.js";
5
- import { guardPath } from "../security/pathGuard.js";
6
- import { redactSensitiveContent } from "../security/contentRedaction.js";
4
+ import { getConfig, getTasksDir } from "../../config.js";
5
+ import { guardPath } from "../../security/pathGuard.js";
6
+ import { redactSensitiveContent } from "../../security/contentRedaction.js";
7
+ import {
8
+ readTextFilePrefixSync,
9
+ readTextFileTailLinesSync,
10
+ } from "../../utils/boundedFile.js";
7
11
 
8
12
  export function getResult(taskId: string): GetTaskFileOutput {
9
13
  return getTaskFile(taskId, "result.md");
@@ -23,26 +27,25 @@ export function getDiff(taskId: string): GetTaskFileOutput {
23
27
  }
24
28
  const filePath = existsSync(preferred) ? preferred : fallback;
25
29
  guardPath(filePath, config.workspaceRoot, config.tasksDir);
26
- const size = statSync(filePath).size;
27
- const raw = readFileSync(filePath, "utf-8");
28
- const patchHead = raw.slice(0, config.maxReadFileBytes);
29
- const rawReturnedBytes = Buffer.byteLength(patchHead, "utf-8");
30
+ const bounded = readTextFilePrefixSync(filePath, config.maxReadFileBytes);
31
+ const size = bounded.totalBytes;
32
+ const patchHead = bounded.content;
30
33
  const redaction = redactSensitiveContent(patchHead);
31
34
  const content = redaction.content;
32
- const diff = {
35
+ const diff: GetTaskFileOutput & { truncated: boolean } = {
33
36
  task_id: taskId,
34
37
  filename: existsSync(preferred) ? "diff.patch" : "git.diff",
35
38
  content,
36
39
  path: filePath,
37
40
  available: true,
38
- truncated: size > rawReturnedBytes,
41
+ truncated: bounded.truncated,
39
42
  total_bytes: size,
40
43
  returned_bytes: Buffer.byteLength(content, "utf-8"),
41
44
  patch_head: content,
42
45
  diff_patch_path: filePath,
43
46
  redacted: redaction.redacted,
44
47
  redaction_categories: redaction.redaction_categories,
45
- } as GetTaskFileOutput;
48
+ } as GetTaskFileOutput & { truncated: boolean };
46
49
  try {
47
50
  const evidence = JSON.parse(getTaskFile(taskId, "changed-files.json").content);
48
51
  return {
@@ -51,7 +54,7 @@ export function getDiff(taskId: string): GetTaskFileOutput {
51
54
  diff_available: Boolean(evidence.diff_available),
52
55
  workspace_dirty_before: Boolean(evidence.workspace_dirty_before),
53
56
  workspace_dirty_after: Boolean(evidence.workspace_dirty_after),
54
- diff_truncated: Boolean(evidence.diff_truncated || (diff as any).truncated),
57
+ diff_truncated: Boolean(evidence.diff_truncated || diff.truncated),
55
58
  diff_size_bytes: Number(evidence.diff_size_bytes || size),
56
59
  additions: Number(evidence.additions || 0),
57
60
  deletions: Number(evidence.deletions || 0),
@@ -101,8 +104,7 @@ export function getTaskLogTail(
101
104
 
102
105
  const stat = statSync(filePath);
103
106
  const totalBytes = stat.size;
104
- const raw = readFileSync(filePath, "utf-8");
105
- const tail = raw.split("\n").slice(-maxLines).join("\n");
107
+ const tail = readTextFileTailLinesSync(filePath, maxLines);
106
108
  const content = applyRedact ? redactSensitiveContent(tail) : { content: tail, redacted: false, redaction_categories: [] as string[] };
107
109
 
108
110
  return {
@@ -134,13 +136,18 @@ function getTaskFileTail(
134
136
  }
135
137
 
136
138
  const stat = statSync(filePath);
137
- const raw = readFileSync(filePath, "utf-8");
138
- let content = raw;
139
+ const maxBytes = options.maxBytes > 0
140
+ ? Math.min(options.maxBytes, config.maxReadFileBytes)
141
+ : config.maxReadFileBytes;
142
+ let content: string;
139
143
  if (options.tailLines > 0) {
140
- content = raw.split("\n").slice(-options.tailLines).join("\n");
141
- }
142
- if (options.maxBytes > 0 && Buffer.byteLength(content, "utf-8") > options.maxBytes) {
143
- content = content.slice(0, options.maxBytes);
144
+ content = readTextFileTailLinesSync(
145
+ filePath,
146
+ Math.min(Math.max(1, options.tailLines), 1000),
147
+ maxBytes,
148
+ );
149
+ } else {
150
+ content = readTextFilePrefixSync(filePath, maxBytes).content;
144
151
  }
145
152
  const redaction = redactSensitiveContent(content);
146
153
  return {
@@ -0,0 +1,54 @@
1
+ export const ACTIVE_TASK_STATUSES = new Set([
2
+ "pending",
3
+ "running",
4
+ "executing_agent",
5
+ "verifying",
6
+ "collecting_artifacts",
7
+ "cancel_requested",
8
+ ]);
9
+
10
+ export const TERMINAL_TASK_STATUSES = new Set([
11
+ "done",
12
+ "done_by_agent",
13
+ "accepted",
14
+ "rejected",
15
+ "needs_fix",
16
+ "blocked",
17
+ "failed",
18
+ "failed_verification",
19
+ "failed_scope_violation",
20
+ "failed_policy_violation",
21
+ "failed_stale",
22
+ "orphaned",
23
+ "timeout",
24
+ "canceled",
25
+ ]);
26
+
27
+ export function isTerminalTaskStatus(status: string): boolean {
28
+ return TERMINAL_TASK_STATUSES.has(status);
29
+ }
30
+
31
+ export function isActiveTaskStatus(status: string): boolean {
32
+ return ACTIVE_TASK_STATUSES.has(status);
33
+ }
34
+
35
+ /** Terminal states are immutable; active states may only advance or terminate. */
36
+ export function isAllowedTaskStatusTransition(from: string, to: string): boolean {
37
+ if (from === to) return true;
38
+ if (isTerminalTaskStatus(from)) return false;
39
+ if (!isActiveTaskStatus(from) || !isActiveTaskStatus(to) && !isTerminalTaskStatus(to)) return false;
40
+
41
+ if (from === "pending") {
42
+ return to === "running" || isTerminalTaskStatus(to);
43
+ }
44
+ if (to === "pending") return false;
45
+
46
+ const forwardActive: Record<string, Set<string>> = {
47
+ running: new Set(["executing_agent", "verifying", "collecting_artifacts", "cancel_requested"]),
48
+ executing_agent: new Set(["verifying", "collecting_artifacts", "cancel_requested"]),
49
+ verifying: new Set(["collecting_artifacts", "cancel_requested"]),
50
+ collecting_artifacts: new Set(["cancel_requested"]),
51
+ cancel_requested: new Set(),
52
+ };
53
+ return isTerminalTaskStatus(to) || Boolean(forwardActive[from]?.has(to));
54
+ }
@@ -1,16 +1,7 @@
1
1
  import { setTimeout as sleep } from "node:timers/promises";
2
2
  import { getTaskStatus } from "./getTaskStatus.js";
3
3
  import { getTaskSummary, type TaskSummaryResult } from "./getTaskSummary.js";
4
-
5
- const TERMINAL_STATUSES = new Set([
6
- "done",
7
- "done_by_agent",
8
- "failed",
9
- "failed_verification",
10
- "failed_scope_violation",
11
- "failed_policy_violation",
12
- "canceled",
13
- ]);
4
+ import { isTerminalTaskStatus } from "./taskStates.js";
14
5
 
15
6
  export interface WaitForTaskProgressSummary {
16
7
  phase: string;
@@ -45,12 +36,12 @@ export async function waitForTask(taskId: string, waitSeconds = 25): Promise<Wai
45
36
  const deadline = started + waitSeconds * 1000;
46
37
  let status = getTaskStatus(taskId);
47
38
 
48
- while (!TERMINAL_STATUSES.has(status.status) && Date.now() < deadline) {
39
+ while (!isTerminalTaskStatus(status.status) && Date.now() < deadline) {
49
40
  await sleep(Math.min(500, Math.max(1, deadline - Date.now())));
50
41
  status = getTaskStatus(taskId);
51
42
  }
52
43
 
53
- const terminal = TERMINAL_STATUSES.has(status.status);
44
+ const terminal = isTerminalTaskStatus(status.status);
54
45
  const executionBlocked = !terminal && status.execution_blocked;
55
46
  const elapsed = Math.round((Date.now() - started) / 1000);
56
47
 
@@ -8,9 +8,9 @@
8
8
  * and inspects files/directories/env vars that already exist.
9
9
  */
10
10
 
11
- import { execSync } from "node:child_process";
12
11
  import { existsSync, readFileSync, statSync } from "node:fs";
13
12
  import { join } from "node:path";
13
+ import { runSimpleProcessSync } from "../../runner/simpleProcess.js";
14
14
 
15
15
  // ── Types ──────────────────────────────────────────────────────────
16
16
 
@@ -47,17 +47,22 @@ const SDK_MISSING_REASON =
47
47
  // ── Helpers ────────────────────────────────────────────────────────
48
48
 
49
49
  /**
50
- * Run a shell command and return its trimmed combined stdout/stderr output.
50
+ * Run a shell-free command and return its trimmed combined stdout/stderr output.
51
51
  * Returns an empty string when the command is missing or fails so callers can
52
52
  * treat absence gracefully. A timeout guards against hanging processes.
53
53
  */
54
- function runCmd(cmdStr: string): string {
54
+ function runCmd(command: string, args: readonly string[], cwd = process.cwd()): string {
55
55
  try {
56
- return execSync(cmdStr, {
57
- encoding: "utf-8",
58
- timeout: 8000,
59
- stdio: ["ignore", "pipe", "ignore"],
60
- }).trim();
56
+ const result = runSimpleProcessSync({
57
+ command,
58
+ args: [...args],
59
+ cwd,
60
+ timeoutMs: 8000,
61
+ maxStdoutBytes: 64 * 1024,
62
+ maxStderrBytes: 64 * 1024,
63
+ });
64
+ if (result.exitCode !== 0) return "";
65
+ return `${result.stdout}\n${result.stderr}`.trim();
61
66
  } catch {
62
67
  return "";
63
68
  }
@@ -128,8 +133,8 @@ export function diagnoseAndroidBuild(repoPath: string): AndroidBuildDiagnostic {
128
133
  const sdkExists = sdkRoot !== "" && isDirectory(sdkRoot);
129
134
 
130
135
  // 1. java -version ------------------------------------------------------
131
- // `java -version` writes to stderr; redirect with 2>&1 so execSync captures it.
132
- const javaOutput = runCmd("java -version 2>&1");
136
+ // `java -version` normally writes to stderr; runSimpleProcessSync captures both.
137
+ const javaOutput = runCmd("java", ["-version"], repoPath);
133
138
  const javaVersion = parseJavaVersion(javaOutput);
134
139
  if (javaVersion) {
135
140
  const major = parseJavaMajor(javaVersion);
@@ -1,19 +1,20 @@
1
- import { getConfig } from "../config.js";
1
+ import { getConfig } from "../../config.js";
2
+ import { PatchWardenError } from "../../errors.js";
2
3
  import {
3
4
  readDirectSession,
4
5
  appendDirectSessionOperation,
5
- } from "../direct/directSessionStore.js";
6
+ withDirectSessionMutationLock,
7
+ } from "../../direct/directSessionStore.js";
6
8
  import {
7
9
  guardDirectSessionActive,
8
10
  guardDirectWritePath,
9
11
  guardDirectPatchSize,
10
- } from "../direct/directGuards.js";
12
+ } from "../../direct/directGuards.js";
11
13
  import {
12
14
  applyPatchOperations,
13
- validateExpectedSha256,
14
15
  computeFileSha256,
15
16
  type PatchOperation,
16
- } from "../direct/directPatch.js";
17
+ } from "../../direct/directPatch.js";
17
18
 
18
19
  export interface ApplyPatchInput {
19
20
  session_id: string;
@@ -33,6 +34,7 @@ export interface ApplyPatchOutput {
33
34
 
34
35
  export function applyPatch(input: ApplyPatchInput): ApplyPatchOutput {
35
36
  const config = getConfig();
37
+ return withDirectSessionMutationLock(input.session_id, () => {
36
38
 
37
39
  // 1. Read session and guard active (not expired, not finalized)
38
40
  const session = readDirectSession(input.session_id);
@@ -47,26 +49,45 @@ export function applyPatch(input: ApplyPatchInput): ApplyPatchOutput {
47
49
  );
48
50
 
49
51
  // 3. Calculate patch size and guard against directMaxPatchBytes
50
- const patchBytes = JSON.stringify(input.operations).length;
52
+ const patchBytes = Buffer.byteLength(JSON.stringify(input.operations), "utf-8");
51
53
  guardDirectPatchSize(patchBytes);
52
54
 
53
- // 4. Validate expected_sha256 matches current file hash
54
- const beforeSha256 = validateExpectedSha256(
55
- resolvedPath,
56
- input.expected_sha256
57
- );
58
-
59
- // 5. Apply patch operations
60
- const patchResult = applyPatchOperations(resolvedPath, input.operations);
55
+ // 4. Apply against the same bytes whose hash is validated. Re-resolve the
56
+ // target immediately before replacement to detect link/reparse-point swaps.
57
+ const patchResult = applyPatchOperations(resolvedPath, input.operations, {
58
+ expectedSha256: input.expected_sha256,
59
+ maxFileBytes: config.directMaxFileBytes,
60
+ revalidatePath: () => guardDirectWritePath(
61
+ input.path,
62
+ session.resolved_repo_path,
63
+ config.workspaceRoot,
64
+ ),
65
+ });
66
+ const beforeSha256 = patchResult.before_sha256;
61
67
 
62
68
  // Re-read the file from disk to compute the authoritative after_sha256
63
- const afterSha256 = computeFileSha256(resolvedPath);
69
+ const finalPath = guardDirectWritePath(
70
+ input.path,
71
+ session.resolved_repo_path,
72
+ config.workspaceRoot,
73
+ );
74
+ const afterSha256 = computeFileSha256(finalPath);
75
+ if (afterSha256 !== patchResult.after_sha256) {
76
+ throw new PatchWardenError(
77
+ "direct_target_changed_after_write",
78
+ `Patch target "${input.path}" changed before the operation could be recorded.`,
79
+ "Inspect the file and retry only after concurrent filesystem changes stop.",
80
+ true,
81
+ { path: input.path, expected_sha256: patchResult.after_sha256, actual_sha256: afterSha256 },
82
+ );
83
+ }
64
84
 
65
85
  // 6. Append operation record to session
66
86
  appendDirectSessionOperation(input.session_id, {
67
- index: session.operations.length,
87
+ index: 0,
68
88
  timestamp: new Date().toISOString(),
69
89
  path: input.path,
90
+ operation_type: "patch",
70
91
  before_sha256: beforeSha256,
71
92
  after_sha256: afterSha256,
72
93
  operations_applied: patchResult.operations_applied,
@@ -83,4 +104,5 @@ export function applyPatch(input: ApplyPatchInput): ApplyPatchOutput {
83
104
  next_action:
84
105
  "Call run_verification to test the changes, or apply_patch for more edits.",
85
106
  };
107
+ }, config);
86
108
  }
@@ -5,9 +5,9 @@
5
5
  * 参考 src/tools/auditTask.ts 的导出/返回风格。
6
6
  */
7
7
 
8
- import { getConfig } from "../config.js";
9
- import { guardWorkspacePath } from "../security/pathGuard.js";
10
- import { discardWorktree } from "../goal/worktreeManager.js";
8
+ import { getConfig } from "../../config.js";
9
+ import { guardWorkspacePath } from "../../security/pathGuard.js";
10
+ import { discardWorktree } from "../../goal/worktreeManager.js";
11
11
 
12
12
  export interface DiscardWorktreeInput {
13
13
  worktree_id: string;
@@ -0,0 +1,10 @@
1
+ export * from "./androidDoctor.js";
2
+ export * from "./applyPatch.js";
3
+ export * from "./discardWorktree.js";
4
+ export * from "./listAgents.js";
5
+ export * from "./listWorkspace.js";
6
+ export * from "./mergeWorktree.js";
7
+ export * from "./readWorkspaceFile.js";
8
+ export * from "./recommendAgentForTask.js";
9
+ export * from "./searchWorkspace.js";
10
+ export * from "./syncFile.js";