@xopcai/xopc 0.0.122 → 0.0.124

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 (273) hide show
  1. package/README.md +28 -11
  2. package/README.zh-CN.md +28 -11
  3. package/dist/browser-ext/manifest.json +1 -1
  4. package/dist/extensions/telegram/xopc.extension.json +1 -1
  5. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-CK3s5cJo.js → agent-avatar-dicebear-B5uSVAyJ.js} +1 -1
  6. package/dist/gateway/static/root/assets/agents-BjIxt67Z.js +2 -0
  7. package/dist/gateway/static/root/assets/{app-management-settings-panel-CWsNHtTs.js → app-management-settings-panel-CYCONLIL.js} +1 -1
  8. package/dist/gateway/static/root/assets/{appearance-settings-C4b8y6iZ.js → appearance-settings-Clw3ryKg.js} +1 -1
  9. package/dist/gateway/static/root/assets/apps-page-QICTeGjX.js +1 -0
  10. package/dist/gateway/static/root/assets/{archive-plugin-DAtNW3Ia.js → archive-plugin-CXUGRp2i.js} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-load-Dm-RY54y.js → attachment-load-Cmiub3Wg.js} +1 -1
  12. package/dist/gateway/static/root/assets/{automation-suggestion-card-BKHewSVT.js → automation-suggestion-card-CU0-4nQ-.js} +1 -1
  13. package/dist/gateway/static/root/assets/automations-page-Car-jtiz.js +1 -0
  14. package/dist/gateway/static/root/assets/{binary-plugins-BG6Xnk71.js → binary-plugins-0Izz-N3p.js} +1 -1
  15. package/dist/gateway/static/root/assets/{block-editor-DfndiO3h.js → block-editor-C1Kv0IkJ.js} +28 -28
  16. package/dist/gateway/static/root/assets/browser-settings-page-CMeBJzlS.js +8 -0
  17. package/dist/gateway/static/root/assets/{capability-presets-api-Cx93GLdw.js → capability-presets-api-BmQTM9Il.js} +1 -1
  18. package/dist/gateway/static/root/assets/capability-presets-settings-panel-DZEJEoCF.js +1 -0
  19. package/dist/gateway/static/root/assets/{channel-recipient-api-DdPTtEs8.js → channel-recipient-api-BeA2aVor.js} +1 -1
  20. package/dist/gateway/static/root/assets/channels-settings-kkW-xFau.js +1 -0
  21. package/dist/gateway/static/root/assets/connectors-page-BvaQvO5F.js +2 -0
  22. package/dist/gateway/static/root/assets/{directory-picker-path-field-D835EHJd.js → directory-picker-path-field-2rjzUu57.js} +1 -1
  23. package/dist/gateway/static/root/assets/{dreaming-settings-CCa1F7Fg.js → dreaming-settings-DnHO686d.js} +1 -1
  24. package/dist/gateway/static/root/assets/{extension-debug-page-W2ITCPSA.js → extension-debug-page-BGLRuWVN.js} +1 -1
  25. package/dist/gateway/static/root/assets/{extension-page-DrvUytvn.js → extension-page-D978ePp9.js} +1 -1
  26. package/dist/gateway/static/root/assets/{extension-settings-page-qM8aLT5u.js → extension-settings-page-B6Ih4Swx.js} +1 -1
  27. package/dist/gateway/static/root/assets/{fetch-ovkvM8KQ.js → fetch-BU5S0Sn7.js} +1 -1
  28. package/dist/gateway/static/root/assets/{gateway-config-api-BB3biuD0.js → gateway-config-api-BIPGwrl1.js} +1 -1
  29. package/dist/gateway/static/root/assets/gateway-settings-CMEA3r5u.js +1 -0
  30. package/dist/gateway/static/root/assets/{gateway-startup-retry-CanQ8UcR.js → gateway-startup-retry-Cd0Zz30N.js} +1 -1
  31. package/dist/gateway/static/root/assets/{goal-create-dialog-CNqNdEkQ.js → goal-create-dialog-B-KHKeXN.js} +1 -1
  32. package/dist/gateway/static/root/assets/goal-detail-page-BxrSoWdj.js +1 -0
  33. package/dist/gateway/static/root/assets/{goals-config-api-BBOX6mUl.js → goals-config-api-B95KdGMj.js} +1 -1
  34. package/dist/gateway/static/root/assets/{goals-page-BP1kfvWv.js → goals-page-BTJzIl28.js} +2 -2
  35. package/dist/gateway/static/root/assets/{goals-settings-Bh3A7OZV.js → goals-settings-BJyEuLLm.js} +1 -1
  36. package/dist/gateway/static/root/assets/heartbeat-settings-wkOjYNQT.js +1 -0
  37. package/dist/gateway/static/root/assets/index-BLaXr6Ev.css +1 -0
  38. package/dist/gateway/static/root/assets/index-c_9TVr_C.js +191 -0
  39. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-B4TKJHla.js → keyboard-shortcuts-settings-BT8T4DyC.js} +1 -1
  40. package/dist/gateway/static/root/assets/logs-page-CrxBJUgI.js +2 -0
  41. package/dist/gateway/static/root/assets/{markdown-split-DcFtdLci.js → markdown-split-Dv66bO2b.js} +1 -1
  42. package/dist/gateway/static/root/assets/{media-plugins-B6SmeLVB.js → media-plugins-BhIQU5yS.js} +1 -1
  43. package/dist/gateway/static/root/assets/messages-O6TAtdFC.js +3 -0
  44. package/dist/gateway/static/root/assets/models-hub-panel-DOOpz6y1.js +2 -0
  45. package/dist/gateway/static/root/assets/notes-page-Br16pK9v.js +1 -0
  46. package/dist/gateway/static/root/assets/notes-workbench-PSJAi0ZC.js +2 -0
  47. package/dist/gateway/static/root/assets/{preview-open-alternatives-C29O19s7.js → preview-open-alternatives-DGzet1d7.js} +1 -1
  48. package/dist/gateway/static/root/assets/project-detail-page-CIGGgKuw.js +1 -0
  49. package/dist/gateway/static/root/assets/projects-page-B2grhsBt.js +1 -0
  50. package/dist/gateway/static/root/assets/remote-access-hub-CXMORxBD.js +1 -0
  51. package/dist/gateway/static/root/assets/{save-bar-controls-B4ad9GfB.js → save-bar-controls-Cqgb-jqa.js} +1 -1
  52. package/dist/gateway/static/root/assets/{schedule-field-DdYncfLR.js → schedule-field-DVZcI8Yt.js} +1 -1
  53. package/dist/gateway/static/root/assets/{schema-form-BMTrDE-7.js → schema-form-Bj7sOxC8.js} +1 -1
  54. package/dist/gateway/static/root/assets/sessions-page-Bi2wkBzU.js +1 -0
  55. package/dist/gateway/static/root/assets/settings-advanced-gate-B1L3G4IA.js +1 -0
  56. package/dist/gateway/static/root/assets/{settings-collapsible-section-MwTW2eK4.js → settings-collapsible-section-BmXn-8N6.js} +1 -1
  57. package/dist/gateway/static/root/assets/{settings-form-section-Dg6LBbcr.js → settings-form-section-BnqYMWHR.js} +1 -1
  58. package/dist/gateway/static/root/assets/settings-page-BfRSHJmO.js +1 -0
  59. package/dist/gateway/static/root/assets/{settings-page-layout-BX4T-OLF.js → settings-page-layout-CdWZU1hd.js} +1 -1
  60. package/dist/gateway/static/root/assets/{setup-status-panel-Ce1pcFtR.js → setup-status-panel-CWtPM7RK.js} +1 -1
  61. package/dist/gateway/static/root/assets/{share-preview-page-DWE-dUFh.js → share-preview-page-IB1v8i86.js} +1 -1
  62. package/dist/gateway/static/root/assets/shares-settings-ngbsBgij.js +1 -0
  63. package/dist/gateway/static/root/assets/skill-reload-api-BIe442Gv.js +1 -0
  64. package/dist/gateway/static/root/assets/skills-page-BfchZRVa.js +2 -0
  65. package/dist/gateway/static/root/assets/{system-settings-panel-Dd6Y35Cm.js → system-settings-panel-BxQ384RC.js} +1 -1
  66. package/dist/gateway/static/root/assets/{typed-models-lib-Cx6XV5A2.js → typed-models-lib-BkcpvFYO.js} +1 -1
  67. package/dist/gateway/static/root/assets/{use-channel-catalog-D4sMbmy7.js → use-channel-catalog-Cd0FaEQ4.js} +1 -1
  68. package/dist/gateway/static/root/assets/{user-profile-settings-panel-Bx-hjMQc.js → user-profile-settings-panel-DQ7NnRSY.js} +1 -1
  69. package/dist/gateway/static/root/assets/{utils-D7b_vZnv.js → utils-YKhVf7Ka.js} +1 -1
  70. package/dist/gateway/static/root/assets/voice-api-key-field-C5Ov8Uk_.js +1 -0
  71. package/dist/gateway/static/root/assets/{workflow-run-setup-panel--TdohF5N.js → workflow-run-setup-panel-BYD9BtOq.js} +2 -2
  72. package/dist/gateway/static/root/assets/{workflow-start-dialog-B0lavx_o.js → workflow-start-dialog-BNJVCWoS.js} +1 -1
  73. package/dist/gateway/static/root/assets/workflows-page-BYF29ANm.js +18 -0
  74. package/dist/gateway/static/root/index.html +5 -5
  75. package/dist/package.js +1 -1
  76. package/dist/src/agent/agent-manager.d.ts +6 -1
  77. package/dist/src/agent/agent-manager.js +28 -12
  78. package/dist/src/agent/agent-manager.js.map +1 -1
  79. package/dist/src/agent/context/workspace-templates/AGENTS.md +1 -1
  80. package/dist/src/agent/embedded/xopc-tools-bridge.js +11 -7
  81. package/dist/src/agent/embedded/xopc-tools-bridge.js.map +1 -1
  82. package/dist/src/agent/lifecycle/progress.js +3 -1
  83. package/dist/src/agent/lifecycle/progress.js.map +1 -1
  84. package/dist/src/agent/lifecycle/timeout-wrapper.d.ts +2 -2
  85. package/dist/src/agent/lifecycle/timeout-wrapper.js +4 -4
  86. package/dist/src/agent/lifecycle/timeout-wrapper.js.map +1 -1
  87. package/dist/src/agent/memory/summary-generator.d.ts +1 -1
  88. package/dist/src/agent/memory/summary-generator.js +1 -2
  89. package/dist/src/agent/memory/summary-generator.js.map +1 -1
  90. package/dist/src/agent/middleware/index.d.ts +1 -1
  91. package/dist/src/agent/middleware/self-verify.d.ts +44 -14
  92. package/dist/src/agent/middleware/self-verify.js +214 -44
  93. package/dist/src/agent/middleware/self-verify.js.map +1 -1
  94. package/dist/src/agent/orchestration/agent-event-handler.js +57 -15
  95. package/dist/src/agent/orchestration/agent-event-handler.js.map +1 -1
  96. package/dist/src/agent/prompt/sections/tooling.js +18 -13
  97. package/dist/src/agent/prompt/sections/tooling.js.map +1 -1
  98. package/dist/src/agent/prompt/system-reminder.js +2 -2
  99. package/dist/src/agent/prompt/system-reminder.js.map +1 -1
  100. package/dist/src/agent/sandbox/sanitize-env-vars.d.ts +2 -2
  101. package/dist/src/agent/sandbox/sanitize-env-vars.js +2 -2
  102. package/dist/src/agent/sandbox/sanitize-env-vars.js.map +1 -1
  103. package/dist/src/agent/sandbox/types.d.ts +2 -2
  104. package/dist/src/agent/sandbox/types.js.map +1 -1
  105. package/dist/src/agent/service/btw-query.js +1 -1
  106. package/dist/src/agent/service/btw-query.js.map +1 -1
  107. package/dist/src/agent/service/process-direct-one-shot.js +6 -2
  108. package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
  109. package/dist/src/agent/service/process-direct-streaming.js +12 -7
  110. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  111. package/dist/src/agent/service.js +2 -1
  112. package/dist/src/agent/service.js.map +1 -1
  113. package/dist/src/agent/skills/skill-tool-gating.js +1 -1
  114. package/dist/src/agent/skills/skill-tool-gating.js.map +1 -1
  115. package/dist/src/agent/starter-agents.js +9 -9
  116. package/dist/src/agent/starter-agents.js.map +1 -1
  117. package/dist/src/agent/tools/apply-patch.d.ts +25 -0
  118. package/dist/src/agent/tools/apply-patch.js +256 -0
  119. package/dist/src/agent/tools/apply-patch.js.map +1 -0
  120. package/dist/src/agent/tools/delegate-tool.d.ts +1 -1
  121. package/dist/src/agent/tools/delegate-tool.js +3 -3
  122. package/dist/src/agent/tools/delegate-tool.js.map +1 -1
  123. package/dist/src/agent/tools/exec-command.d.ts +30 -0
  124. package/dist/src/agent/tools/exec-command.js +224 -0
  125. package/dist/src/agent/tools/exec-command.js.map +1 -0
  126. package/dist/src/agent/tools/execute-code-tool.js +7 -4
  127. package/dist/src/agent/tools/execute-code-tool.js.map +1 -1
  128. package/dist/src/agent/tools/factory.d.ts +3 -3
  129. package/dist/src/agent/tools/factory.js +8 -9
  130. package/dist/src/agent/tools/factory.js.map +1 -1
  131. package/dist/src/agent/tools/goal-tool.d.ts +5 -1
  132. package/dist/src/agent/tools/goal-tool.js +8 -2
  133. package/dist/src/agent/tools/goal-tool.js.map +1 -1
  134. package/dist/src/agent/tools/index.d.ts +4 -3
  135. package/dist/src/agent/tools/index.js +5 -4
  136. package/dist/src/agent/tools/skills-tools.d.ts +1 -1
  137. package/dist/src/agent/tools/skills-tools.js.map +1 -1
  138. package/dist/src/agent/tools/update-plan-tool.d.ts +11 -0
  139. package/dist/src/agent/tools/update-plan-tool.js +72 -0
  140. package/dist/src/agent/tools/update-plan-tool.js.map +1 -0
  141. package/dist/src/agent/workflow/builtins/debug-incident.d.ts +1 -1
  142. package/dist/src/agent/workflow/builtins/debug-incident.js +1 -1
  143. package/dist/src/agent/workflow/builtins/debug-incident.js.map +1 -1
  144. package/dist/src/agent/workflow/builtins/pr-review.d.ts +1 -1
  145. package/dist/src/agent/workflow/builtins/pr-review.js +1 -1
  146. package/dist/src/agent/workflow/builtins/pr-review.js.map +1 -1
  147. package/dist/src/agent/workflow/builtins/release-check.d.ts +1 -1
  148. package/dist/src/agent/workflow/builtins/release-check.js +1 -1
  149. package/dist/src/agent/workflow/builtins/release-check.js.map +1 -1
  150. package/dist/src/agent/workflow/step-labels.js +7 -3
  151. package/dist/src/agent/workflow/step-labels.js.map +1 -1
  152. package/dist/src/chat-commands/agent-edit.js +1 -1
  153. package/dist/src/chat-commands/agent-edit.js.map +1 -1
  154. package/dist/src/chat-commands/builtins/goal.js +4 -1
  155. package/dist/src/chat-commands/builtins/goal.js.map +1 -1
  156. package/dist/src/chat-commands/builtins/review.d.ts +1 -0
  157. package/dist/src/chat-commands/builtins/review.js +167 -0
  158. package/dist/src/chat-commands/builtins/review.js.map +1 -0
  159. package/dist/src/chat-commands/index.d.ts +1 -0
  160. package/dist/src/chat-commands/index.js +3 -1
  161. package/dist/src/chat-commands/index.js.map +1 -1
  162. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  163. package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
  164. package/dist/src/config/agent-profile.js +1 -1
  165. package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
  166. package/dist/src/gateway/agent-builtin-tools.js +3 -2
  167. package/dist/src/gateway/agent-builtin-tools.js.map +1 -1
  168. package/dist/src/gateway/agents-admin.js +1 -1
  169. package/dist/src/gateway/chat-stream/mapper.d.ts +6 -0
  170. package/dist/src/gateway/chat-stream/mapper.js +149 -6
  171. package/dist/src/gateway/chat-stream/mapper.js.map +1 -1
  172. package/dist/src/gateway/chat-stream/protocol.d.ts +51 -1
  173. package/dist/src/gateway/hono/routes/commands-skills.js +21 -0
  174. package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
  175. package/dist/src/gateway/hono/routes/index.js +2 -0
  176. package/dist/src/gateway/hono/routes/index.js.map +1 -1
  177. package/dist/src/gateway/hono/routes/lazy-bundles.js +1 -0
  178. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  179. package/dist/src/gateway/hono/routes/projects.js +136 -14
  180. package/dist/src/gateway/hono/routes/projects.js.map +1 -1
  181. package/dist/src/gateway/hono/routes/search.d.ts +3 -0
  182. package/dist/src/gateway/hono/routes/search.js +47 -0
  183. package/dist/src/gateway/hono/routes/search.js.map +1 -0
  184. package/dist/src/gateway/security/dangerous-tools.js +1 -1
  185. package/dist/src/gateway/security/dangerous-tools.js.map +1 -1
  186. package/dist/src/infra/update-restart.js +1 -1
  187. package/dist/src/projects/index.d.ts +3 -0
  188. package/dist/src/projects/index.js +4 -1
  189. package/dist/src/projects/project-agent-suggestion.d.ts +14 -0
  190. package/dist/src/projects/project-agent-suggestion.js +19 -0
  191. package/dist/src/projects/project-agent-suggestion.js.map +1 -0
  192. package/dist/src/projects/project-kind.d.ts +13 -0
  193. package/dist/src/projects/project-kind.js +147 -0
  194. package/dist/src/projects/project-kind.js.map +1 -0
  195. package/dist/src/projects/project-service.d.ts +15 -0
  196. package/dist/src/projects/project-service.js +96 -3
  197. package/dist/src/projects/project-service.js.map +1 -1
  198. package/dist/src/projects/project-store.js +32 -8
  199. package/dist/src/projects/project-store.js.map +1 -1
  200. package/dist/src/projects/types.d.ts +4 -1
  201. package/dist/src/projects/workspace-project.d.ts +22 -0
  202. package/dist/src/projects/workspace-project.js +143 -0
  203. package/dist/src/projects/workspace-project.js.map +1 -0
  204. package/dist/src/review/review-git.d.ts +48 -0
  205. package/dist/src/review/review-git.js +333 -0
  206. package/dist/src/review/review-git.js.map +1 -0
  207. package/dist/src/review/review-types.d.ts +22 -0
  208. package/dist/src/review/review-types.js +29 -0
  209. package/dist/src/review/review-types.js.map +1 -0
  210. package/dist/src/session/session-context-for-llm.js +151 -0
  211. package/dist/src/session/session-context-for-llm.js.map +1 -1
  212. package/dist/src/storage/sqlite/migrations/021_projects_fts.sql +21 -0
  213. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  214. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  215. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  216. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  217. package/dist/src/storage/sqlite/schema.js +1 -1
  218. package/dist/src/tui/backends/embedded-backend.d.ts +8 -1
  219. package/dist/src/tui/backends/embedded-backend.js +60 -1
  220. package/dist/src/tui/backends/embedded-backend.js.map +1 -1
  221. package/dist/src/tui/backends/gateway-sse-backend.d.ts +9 -1
  222. package/dist/src/tui/backends/gateway-sse-backend.js +29 -0
  223. package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
  224. package/dist/src/tui/components/assistant-message.js +35 -0
  225. package/dist/src/tui/components/assistant-message.js.map +1 -1
  226. package/dist/src/tui/tui-agent-events.js +25 -0
  227. package/dist/src/tui/tui-agent-events.js.map +1 -1
  228. package/dist/src/tui/tui-backend.d.ts +20 -0
  229. package/dist/src/tui/tui-commands.d.ts +1 -0
  230. package/dist/src/tui/tui-commands.js +110 -4
  231. package/dist/src/tui/tui-commands.js.map +1 -1
  232. package/dist/src/tui/tui-picker-overlay.d.ts +2 -0
  233. package/dist/src/tui/tui-picker-overlay.js +153 -1
  234. package/dist/src/tui/tui-picker-overlay.js.map +1 -1
  235. package/dist/src/tui/tui.js +36 -2
  236. package/dist/src/tui/tui.js.map +1 -1
  237. package/dist/src/workflows/service/workflow-run-service.js +1 -1
  238. package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
  239. package/package.json +1 -1
  240. package/dist/gateway/static/root/assets/agents-SsQrj9B-.js +0 -2
  241. package/dist/gateway/static/root/assets/apps-page-CYb6fame.js +0 -1
  242. package/dist/gateway/static/root/assets/automations-page-DpOOq1nk.js +0 -1
  243. package/dist/gateway/static/root/assets/browser-settings-page-9nXxgh4u.js +0 -8
  244. package/dist/gateway/static/root/assets/capability-presets-settings-panel-D528_YFy.js +0 -1
  245. package/dist/gateway/static/root/assets/channels-settings-Ck-3h5lI.js +0 -1
  246. package/dist/gateway/static/root/assets/connectors-page-DL-fAqE_.js +0 -2
  247. package/dist/gateway/static/root/assets/gateway-settings-mmmnIH27.js +0 -1
  248. package/dist/gateway/static/root/assets/goal-detail-page-Ceu5zrN6.js +0 -1
  249. package/dist/gateway/static/root/assets/heartbeat-settings-YoYgKlfX.js +0 -1
  250. package/dist/gateway/static/root/assets/index-DdqSO9R2.js +0 -191
  251. package/dist/gateway/static/root/assets/index-jGB-2nI-.css +0 -1
  252. package/dist/gateway/static/root/assets/logs-page-BgVk5DvZ.js +0 -2
  253. package/dist/gateway/static/root/assets/messages-B5O4OIEo.js +0 -3
  254. package/dist/gateway/static/root/assets/models-hub-panel-BU0x8PGP.js +0 -2
  255. package/dist/gateway/static/root/assets/notes-page-D6JYVP4I.js +0 -1
  256. package/dist/gateway/static/root/assets/notes-workbench-mqRJYnJS.js +0 -2
  257. package/dist/gateway/static/root/assets/project-detail-page-DOTgzoeh.js +0 -1
  258. package/dist/gateway/static/root/assets/projects-page-DGUIb4y5.js +0 -1
  259. package/dist/gateway/static/root/assets/remote-access-hub-DVKKUj3u.js +0 -1
  260. package/dist/gateway/static/root/assets/sessions-page-BVbzUWxW.js +0 -1
  261. package/dist/gateway/static/root/assets/settings-advanced-gate-DrNNoSqE.js +0 -1
  262. package/dist/gateway/static/root/assets/settings-page-CMDqgeMR.js +0 -1
  263. package/dist/gateway/static/root/assets/shares-settings-aKSraR8l.js +0 -1
  264. package/dist/gateway/static/root/assets/skill-reload-api-CS-FU_B-.js +0 -1
  265. package/dist/gateway/static/root/assets/skills-page-BaF3u0LA.js +0 -2
  266. package/dist/gateway/static/root/assets/voice-api-key-field-CkhbRt1q.js +0 -1
  267. package/dist/gateway/static/root/assets/workflows-page-CZR05Bu_.js +0 -18
  268. package/dist/src/agent/tools/edit.d.ts +0 -14
  269. package/dist/src/agent/tools/edit.js +0 -132
  270. package/dist/src/agent/tools/edit.js.map +0 -1
  271. package/dist/src/agent/tools/shell.d.ts +0 -15
  272. package/dist/src/agent/tools/shell.js +0 -158
  273. package/dist/src/agent/tools/shell.js.map +0 -1
@@ -9,64 +9,161 @@ const DEFAULT_CONFIG = {
9
9
  minTurnsForVerification: 3,
10
10
  resetOnVerification: true
11
11
  };
12
+ function createTrackerState() {
13
+ return {
14
+ fileEdits: /* @__PURE__ */ new Map(),
15
+ turnCount: 0,
16
+ verificationRequested: false,
17
+ hasUnverifiedEdits: false,
18
+ diffReviewed: false,
19
+ lastVerification: void 0,
20
+ lastMutationTool: void 0
21
+ };
22
+ }
12
23
  /**
13
24
  * Tracks file modifications and provides verification guidance
14
25
  */
15
26
  var SelfVerifyMiddleware = class {
16
- fileEdits = /* @__PURE__ */ new Map();
27
+ states = /* @__PURE__ */ new Map();
17
28
  config;
18
- turnCount = 0;
19
- verificationRequested = false;
20
- hasUnverifiedEdits = false;
21
29
  constructor(config = {}) {
22
30
  this.config = {
23
31
  ...DEFAULT_CONFIG,
24
32
  ...config
25
33
  };
26
34
  }
35
+ getState(sessionKey) {
36
+ const key = sessionKey ?? "default";
37
+ let state = this.states.get(key);
38
+ if (!state) {
39
+ state = createTrackerState();
40
+ this.states.set(key, state);
41
+ }
42
+ return state;
43
+ }
27
44
  /**
28
45
  * Record a file edit operation
29
46
  */
30
- recordEdit(filePath, operation) {
47
+ recordEdit(filePath, operation, sessionKey) {
48
+ const state = this.getState(sessionKey);
31
49
  const now = Date.now();
32
- const existing = this.fileEdits.get(filePath);
50
+ const existing = state.fileEdits.get(filePath);
33
51
  if (existing) {
34
52
  existing.editCount++;
35
53
  existing.lastEditTime = now;
36
54
  if (!existing.operations.includes(operation)) existing.operations.push(operation);
37
- } else this.fileEdits.set(filePath, {
55
+ } else state.fileEdits.set(filePath, {
38
56
  path: filePath,
39
57
  editCount: 1,
40
58
  lastEditTime: now,
41
59
  operations: [operation]
42
60
  });
43
- this.hasUnverifiedEdits = true;
61
+ state.hasUnverifiedEdits = true;
62
+ state.diffReviewed = false;
63
+ state.lastMutationTool = operation;
44
64
  log.debug({
45
65
  filePath,
46
- editCount: this.getEditCount(filePath),
66
+ editCount: this.getEditCount(filePath, sessionKey),
47
67
  operation
48
68
  }, "File edit recorded");
49
69
  }
50
- recordVerification(toolName, args) {
70
+ recordVerification(toolName, args, outcome, sessionKey) {
71
+ const state = this.getState(sessionKey);
51
72
  const name = toolName.toLowerCase();
52
- if (name === "shell") {
53
- const command = args && typeof args === "object" && typeof args.command === "string" ? args.command.toLowerCase() : "";
54
- if (/\b(test|vitest|jest|mocha|ava|playwright|typecheck|tsc|build|lint)\b/.test(command)) this.hasUnverifiedEdits = false;
73
+ if (name === "exec_command") {
74
+ const command = this.extractExecCommand(args, outcome?.result);
75
+ const success = this.isSuccessfulExecVerification(outcome);
76
+ const details = this.extractResultDetails(outcome?.result);
77
+ if (success && this.isDiffReviewCommand(command)) {
78
+ state.diffReviewed = true;
79
+ return;
80
+ }
81
+ if (!this.isVerificationCommand(command)) return;
82
+ const rawExitCode = details?.exitCode;
83
+ let exitCode;
84
+ if (typeof rawExitCode === "number") exitCode = rawExitCode;
85
+ else if (rawExitCode === null) exitCode = null;
86
+ state.lastVerification = {
87
+ toolName,
88
+ command,
89
+ success,
90
+ exitCode,
91
+ status: typeof details?.status === "string" ? details.status : void 0,
92
+ timedOut: details?.timedOut === true
93
+ };
94
+ if (success) state.hasUnverifiedEdits = false;
55
95
  return;
56
96
  }
57
- if (name.includes("test") || name.includes("verify") || name.includes("lint")) this.hasUnverifiedEdits = false;
97
+ if (name.includes("test") || name.includes("verify") || name.includes("lint")) {
98
+ state.hasUnverifiedEdits = false;
99
+ state.lastVerification = {
100
+ toolName,
101
+ success: true
102
+ };
103
+ }
104
+ }
105
+ getVerificationState(sessionKey) {
106
+ const state = this.getState(sessionKey);
107
+ const changedFiles = Array.from(state.fileEdits.keys()).sort();
108
+ const lastVerification = state.lastVerification ? { ...state.lastVerification } : void 0;
109
+ return {
110
+ hasUnverifiedEdits: state.hasUnverifiedEdits,
111
+ changedFiles,
112
+ diffReviewed: state.diffReviewed,
113
+ verificationAttempted: Boolean(lastVerification),
114
+ lastVerificationFailed: Boolean(lastVerification && !lastVerification.success),
115
+ lastVerification,
116
+ lastMutationTool: state.lastMutationTool
117
+ };
118
+ }
119
+ getPendingVerificationContext(sessionKey, agentId) {
120
+ const state = this.getState(sessionKey);
121
+ if (!this.config.enablePreCompletionCheck || !state.hasUnverifiedEdits) return "";
122
+ return this.buildPendingVerificationReminder(sessionKey, agentId);
123
+ }
124
+ extractExecCommand(args, result) {
125
+ if (args && typeof args === "object") {
126
+ const rec = args;
127
+ if (typeof rec.cmd === "string") return rec.cmd.toLowerCase();
128
+ if (typeof rec.command === "string") return rec.command.toLowerCase();
129
+ }
130
+ const details = this.extractResultDetails(result);
131
+ if (details) {
132
+ const command = details.command;
133
+ if (typeof command === "string") return command.toLowerCase();
134
+ }
135
+ return "";
136
+ }
137
+ extractResultDetails(result) {
138
+ if (!result || typeof result !== "object") return null;
139
+ const details = result.details;
140
+ return details && typeof details === "object" ? details : null;
141
+ }
142
+ isSuccessfulExecVerification(outcome) {
143
+ if (!outcome || outcome.isError) return false;
144
+ const details = this.extractResultDetails(outcome.result);
145
+ if (!details) return false;
146
+ if (details.timedOut === true) return false;
147
+ if (details.status === "success") return true;
148
+ return details.exitCode === 0;
149
+ }
150
+ isVerificationCommand(command) {
151
+ return /\b(test|vitest|jest|mocha|ava|playwright|typecheck|type-check|tsc|build|lint)\b/.test(command);
152
+ }
153
+ isDiffReviewCommand(command) {
154
+ return /\bgit\s+(diff|status)\b/.test(command);
58
155
  }
59
156
  /**
60
157
  * Get edit count for a specific file
61
158
  */
62
- getEditCount(filePath) {
63
- return this.fileEdits.get(filePath)?.editCount || 0;
159
+ getEditCount(filePath, sessionKey) {
160
+ return this.getState(sessionKey).fileEdits.get(filePath)?.editCount || 0;
64
161
  }
65
162
  /**
66
163
  * Check if any file has excessive edits (potential doom loop)
67
164
  */
68
- hasExcessiveEdits() {
69
- for (const [path, record] of this.fileEdits.entries()) if (record.editCount >= this.config.maxEditsPerFile) return {
165
+ hasExcessiveEdits(sessionKey) {
166
+ for (const [path, record] of this.getState(sessionKey).fileEdits.entries()) if (record.editCount >= this.config.maxEditsPerFile) return {
70
167
  file: path,
71
168
  count: record.editCount
72
169
  };
@@ -75,52 +172,55 @@ var SelfVerifyMiddleware = class {
75
172
  /**
76
173
  * Increment turn counter
77
174
  */
78
- onTurnStart() {
79
- this.turnCount++;
80
- this.verificationRequested = false;
175
+ onTurnStart(sessionKey) {
176
+ const state = this.getState(sessionKey);
177
+ state.turnCount++;
178
+ state.verificationRequested = false;
81
179
  }
82
180
  /**
83
181
  * Reset all tracking (e.g., after successful task completion)
84
182
  */
85
- reset() {
86
- this.fileEdits.clear();
87
- this.turnCount = 0;
88
- this.verificationRequested = false;
89
- this.hasUnverifiedEdits = false;
183
+ reset(sessionKey) {
184
+ if (sessionKey) this.states.set(sessionKey, createTrackerState());
185
+ else this.states.clear();
90
186
  log.debug("Self-verify middleware reset");
91
187
  }
92
- consumePostEditReminder() {
93
- if (!this.config.enablePreCompletionCheck || !this.hasUnverifiedEdits) return "";
94
- if (this.verificationRequested) return "";
95
- this.verificationRequested = true;
96
- return ["Coder check: source files changed. Before final response, inspect the diff and run the smallest meaningful verification; if you cannot, explain why and state the remaining risk."].join("\n");
188
+ consumePostEditReminder(sessionKey) {
189
+ const state = this.getState(sessionKey);
190
+ if (!this.config.enablePreCompletionCheck || !state.hasUnverifiedEdits) return "";
191
+ if (state.verificationRequested) return "";
192
+ state.verificationRequested = true;
193
+ return this.buildPendingVerificationReminder(sessionKey);
97
194
  }
98
195
  /**
99
196
  * Get context injection for system prompt
100
197
  * Adds verification guidance based on current state
101
198
  */
102
- getContextInjection() {
199
+ getContextInjection(sessionKey, agentId) {
103
200
  const sections = [];
104
201
  sections.push(this.buildWorkflowGuidance());
105
- const excessive = this.hasExcessiveEdits();
202
+ const excessive = this.hasExcessiveEdits(sessionKey);
106
203
  if (excessive) sections.push(this.buildExcessiveEditWarning(excessive.file, excessive.count));
107
- if (this.shouldPromptForVerification()) sections.push(this.buildPreCompletionReminder());
204
+ if (this.shouldPromptForVerification(sessionKey)) sections.push(this.buildPreCompletionReminder(sessionKey));
205
+ const pending = this.getPendingVerificationContext(sessionKey, agentId);
206
+ if (pending) sections.push(pending);
108
207
  return sections.filter(Boolean).join("\n\n");
109
208
  }
110
209
  /**
111
210
  * Check if we should inject verification reminder
112
211
  */
113
- shouldPromptForVerification() {
212
+ shouldPromptForVerification(sessionKey) {
213
+ const state = this.getState(sessionKey);
114
214
  if (!this.config.enablePreCompletionCheck) return false;
115
- if (this.turnCount < this.config.minTurnsForVerification) return false;
116
- if (this.verificationRequested) return false;
215
+ if (state.turnCount < this.config.minTurnsForVerification) return false;
216
+ if (state.verificationRequested) return false;
117
217
  return true;
118
218
  }
119
219
  /**
120
220
  * Mark verification as requested (to avoid spam)
121
221
  */
122
- markVerificationRequested() {
123
- this.verificationRequested = true;
222
+ markVerificationRequested(sessionKey) {
223
+ this.getState(sessionKey).verificationRequested = true;
124
224
  }
125
225
  /**
126
226
  * Build workflow guidance section
@@ -143,6 +243,75 @@ Follow this iterative process for all tasks:
143
243
 
144
244
  Do not skip verification. Incomplete verification leads to incorrect solutions.`;
145
245
  }
246
+ classifyAgent(agentId) {
247
+ const id = agentId?.trim().toLowerCase() ?? "";
248
+ if (!id) return "generic";
249
+ if (id.includes("coder") || id.includes("code") || id.includes("developer")) return "coder";
250
+ if (id.includes("data") || id.includes("analyst") || id.includes("analysis")) return "data";
251
+ if (id.includes("writer") || id.includes("writing")) return "writer";
252
+ if (id.includes("research")) return "researcher";
253
+ if (id.includes("creative") || id.includes("design")) return "creative";
254
+ return "generic";
255
+ }
256
+ buildPendingVerificationReminder(sessionKey, agentId) {
257
+ const state = this.getVerificationState(sessionKey);
258
+ const kind = this.classifyAgent(agentId);
259
+ const files = state.changedFiles.length > 0 ? state.changedFiles.slice(0, 12).join(", ") : "(unknown files)";
260
+ const extraFiles = state.changedFiles.length > 12 ? `, and ${state.changedFiles.length - 12} more` : "";
261
+ const profile = this.verificationProfile(kind);
262
+ const lines = [
263
+ `## ${profile.title}`,
264
+ profile.checkLine,
265
+ `${profile.changedLabel}: ${files}${extraFiles}.`,
266
+ state.diffReviewed ? "Diff review: completed with git diff/status." : "Diff review: not recorded yet."
267
+ ];
268
+ if (state.lastVerification) {
269
+ const command = state.lastVerification.command ? ` (${state.lastVerification.command})` : "";
270
+ lines.push(state.lastVerification.success ? `Last verification: passed${command}.` : `Last verification: failed${command}.`);
271
+ } else lines.push("Last verification: none recorded.");
272
+ lines.push(profile.finalGuidance);
273
+ return lines.join("\n");
274
+ }
275
+ verificationProfile(kind) {
276
+ switch (kind) {
277
+ case "coder": return {
278
+ title: "Coder Verification State",
279
+ checkLine: "Coder check: source files changed.",
280
+ changedLabel: "Changed source files",
281
+ finalGuidance: "Before final response, inspect the diff and run the smallest meaningful code verification, such as a targeted test, typecheck, lint, or build. If verification cannot run, final response must state why and name the remaining risk."
282
+ };
283
+ case "data": return {
284
+ title: "Data Verification State",
285
+ checkLine: "Data check: analysis files or outputs changed.",
286
+ changedLabel: "Changed files or artifacts",
287
+ finalGuidance: "Before final response, inspect the diff or generated outputs and run the smallest meaningful data validation, such as rerunning the analysis, checking row counts, schemas, filters, joins, units, or output files. If validation cannot run, final response must state why and name the remaining risk."
288
+ };
289
+ case "writer": return {
290
+ title: "Writing Review State",
291
+ checkLine: "Writing check: draft or document files changed.",
292
+ changedLabel: "Changed files or artifacts",
293
+ finalGuidance: "Before final response, inspect the changed artifact for requested content, structure, formatting, placeholders, and obvious inconsistencies. If a review cannot be completed, final response must state why and name the remaining risk."
294
+ };
295
+ case "researcher": return {
296
+ title: "Research Review State",
297
+ checkLine: "Research check: research notes or report files changed.",
298
+ changedLabel: "Changed files or artifacts",
299
+ finalGuidance: "Before final response, inspect the changed artifact and verify citations, source-backed claims, unresolved uncertainty, and formatting. If source or artifact review cannot be completed, final response must state why and name the remaining risk."
300
+ };
301
+ case "creative": return {
302
+ title: "Creative Review State",
303
+ checkLine: "Creative check: creative artifact files changed.",
304
+ changedLabel: "Changed files or artifacts",
305
+ finalGuidance: "Before final response, inspect the changed artifact against the brief, requested format, visual/text quality, and any saved outputs. If review cannot be completed, final response must state why and name the remaining risk."
306
+ };
307
+ default: return {
308
+ title: "Workspace Verification State",
309
+ checkLine: "Workspace check: files changed.",
310
+ changedLabel: "Changed files or artifacts",
311
+ finalGuidance: "Before final response, inspect the changed files and run the smallest meaningful verification for this task. If verification cannot run, final response must state why and name the remaining risk."
312
+ };
313
+ }
314
+ }
146
315
  /**
147
316
  * Build warning for excessive file edits (doom loop detection)
148
317
  */
@@ -162,9 +331,10 @@ This may indicate:
162
331
  /**
163
332
  * Build pre-completion verification reminder
164
333
  */
165
- buildPreCompletionReminder() {
166
- this.markVerificationRequested();
167
- return `⏳ **Verification Check**: You've made ${this.turnCount} turns on this task.
334
+ buildPreCompletionReminder(sessionKey) {
335
+ const state = this.getState(sessionKey);
336
+ this.markVerificationRequested(sessionKey);
337
+ return `⏳ **Verification Check**: You've made ${state.turnCount} turns on this task.
168
338
 
169
339
  Before completing:
170
340
  1. ✅ Verify your solution matches the original requirements
@@ -177,8 +347,8 @@ If you're confident, proceed. If unsure, continue refining.`;
177
347
  /**
178
348
  * Get summary of file edits for debugging
179
349
  */
180
- getEditSummary() {
181
- const entries = Array.from(this.fileEdits.entries());
350
+ getEditSummary(sessionKey) {
351
+ const entries = Array.from(this.getState(sessionKey).fileEdits.entries());
182
352
  const totalEdits = entries.reduce((sum, [, record]) => sum + record.editCount, 0);
183
353
  const topFiles = entries.sort((a, b) => b[1].editCount - a[1].editCount).slice(0, 5).map(([path, record]) => ({
184
354
  path,
@@ -1 +1 @@
1
- {"version":3,"file":"self-verify.js","names":[],"sources":["../../../../src/agent/middleware/self-verify.ts"],"sourcesContent":["/**\n * Self-Verification Middleware\n *\n * Implements the \"Build & Self-Verify\" pattern from harness engineering:\n * - Intercepts agent before completion to remind verification\n * - Tracks file edits to detect potential issues\n * - Injects verification reminders into context\n *\n * Inspired by: https://blog.langchain.com/improving-deep-agents-with-harness-engineering/\n */\n\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('SelfVerifyMiddleware');\n\nexport interface FileEditRecord {\n path: string;\n editCount: number;\n lastEditTime: number;\n operations: string[]; // 'write', 'edit', 'shell' (for build commands)\n}\n\nexport interface SelfVerifyConfig {\n /** Max edits to same file before warning (default: 5) */\n maxEditsPerFile: number;\n /** Enable pre-completion verification check (default: true) */\n enablePreCompletionCheck: boolean;\n /** Min turns before triggering verification reminder (default: 3) */\n minTurnsForVerification: number;\n /** Reset counters on successful verification (default: true) */\n resetOnVerification: boolean;\n}\n\nconst DEFAULT_CONFIG: SelfVerifyConfig = {\n maxEditsPerFile: 5,\n enablePreCompletionCheck: true,\n minTurnsForVerification: 3,\n resetOnVerification: true,\n};\n\n/**\n * Tracks file modifications and provides verification guidance\n */\nexport class SelfVerifyMiddleware {\n private fileEdits: Map<string, FileEditRecord> = new Map();\n private config: SelfVerifyConfig;\n private turnCount = 0;\n private verificationRequested = false;\n private hasUnverifiedEdits = false;\n\n constructor(config: Partial<SelfVerifyConfig> = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n /**\n * Record a file edit operation\n */\n recordEdit(filePath: string, operation: 'write' | 'edit' | 'shell'): void {\n const now = Date.now();\n const existing = this.fileEdits.get(filePath);\n\n if (existing) {\n existing.editCount++;\n existing.lastEditTime = now;\n if (!existing.operations.includes(operation)) {\n existing.operations.push(operation);\n }\n } else {\n this.fileEdits.set(filePath, {\n path: filePath,\n editCount: 1,\n lastEditTime: now,\n operations: [operation],\n });\n }\n\n this.hasUnverifiedEdits = true;\n log.debug({ filePath, editCount: this.getEditCount(filePath), operation }, 'File edit recorded');\n }\n\n recordVerification(toolName: string, args?: unknown): void {\n const name = toolName.toLowerCase();\n if (name === 'shell') {\n const command =\n args && typeof args === 'object' && typeof (args as { command?: unknown }).command === 'string'\n ? (args as { command: string }).command.toLowerCase()\n : '';\n if (/\\b(test|vitest|jest|mocha|ava|playwright|typecheck|tsc|build|lint)\\b/.test(command)) {\n this.hasUnverifiedEdits = false;\n }\n return;\n }\n if (name.includes('test') || name.includes('verify') || name.includes('lint')) {\n this.hasUnverifiedEdits = false;\n }\n }\n\n /**\n * Get edit count for a specific file\n */\n getEditCount(filePath: string): number {\n return this.fileEdits.get(filePath)?.editCount || 0;\n }\n\n /**\n * Check if any file has excessive edits (potential doom loop)\n */\n hasExcessiveEdits(): { file: string; count: number } | null {\n for (const [path, record] of this.fileEdits.entries()) {\n if (record.editCount >= this.config.maxEditsPerFile) {\n return { file: path, count: record.editCount };\n }\n }\n return null;\n }\n\n /**\n * Increment turn counter\n */\n onTurnStart(): void {\n this.turnCount++;\n this.verificationRequested = false;\n }\n\n /**\n * Reset all tracking (e.g., after successful task completion)\n */\n reset(): void {\n this.fileEdits.clear();\n this.turnCount = 0;\n this.verificationRequested = false;\n this.hasUnverifiedEdits = false;\n log.debug('Self-verify middleware reset');\n }\n\n consumePostEditReminder(): string {\n if (!this.config.enablePreCompletionCheck || !this.hasUnverifiedEdits) {\n return '';\n }\n if (this.verificationRequested) {\n return '';\n }\n this.verificationRequested = true;\n return [\n 'Coder check: source files changed. Before final response, inspect the diff and run the smallest meaningful verification; if you cannot, explain why and state the remaining risk.',\n ].join('\\n');\n }\n\n /**\n * Get context injection for system prompt\n * Adds verification guidance based on current state\n */\n getContextInjection(): string {\n const sections: string[] = [];\n\n // Add workflow guidance\n sections.push(this.buildWorkflowGuidance());\n\n // Add excessive edit warning if needed\n const excessive = this.hasExcessiveEdits();\n if (excessive) {\n sections.push(this.buildExcessiveEditWarning(excessive.file, excessive.count));\n }\n\n // Add pre-completion check reminder for long sessions\n if (this.shouldPromptForVerification()) {\n sections.push(this.buildPreCompletionReminder());\n }\n\n return sections.filter(Boolean).join('\\n\\n');\n }\n\n /**\n * Check if we should inject verification reminder\n */\n private shouldPromptForVerification(): boolean {\n if (!this.config.enablePreCompletionCheck) return false;\n if (this.turnCount < this.config.minTurnsForVerification) return false;\n if (this.verificationRequested) return false;\n return true;\n }\n\n /**\n * Mark verification as requested (to avoid spam)\n */\n markVerificationRequested(): void {\n this.verificationRequested = true;\n }\n\n /**\n * Build workflow guidance section\n */\n private buildWorkflowGuidance(): string {\n return `## Problem Solving Workflow\n\nFollow this iterative process for all tasks:\n\n1. **Plan**: Understand the task, read relevant files, and create a plan\n2. **Build**: Implement your solution with verification in mind\n3. **Verify**: Test your work, run checks, compare against requirements\n4. **Fix**: If issues found, analyze and fix them\n\n**Important**: Before declaring a task complete:\n- Re-read the original requirements\n- Verify your solution meets ALL requirements\n- Run any available tests or validation\n- Check for edge cases\n\nDo not skip verification. Incomplete verification leads to incorrect solutions.`;\n }\n\n /**\n * Build warning for excessive file edits (doom loop detection)\n */\n private buildExcessiveEditWarning(filePath: string, count: number): string {\n return `⚠️ **Pattern Alert**: You have edited \"${filePath}\" ${count} times.\n\nThis may indicate:\n- You're fixing symptoms rather than root causes\n- The approach needs reconsideration\n- Requirements may be unclear\n\n**Recommendation**: \n- Step back and re-read the original task\n- Consider if there's a better approach\n- Verify you understand the requirements correctly`;\n }\n\n /**\n * Build pre-completion verification reminder\n */\n private buildPreCompletionReminder(): string {\n this.markVerificationRequested();\n return `⏳ **Verification Check**: You've made ${this.turnCount} turns on this task.\n\nBefore completing:\n1. ✅ Verify your solution matches the original requirements\n2. ✅ Run tests or validation if available\n3. ✅ Check edge cases and error handling\n4. ✅ Review your changes one final time\n\nIf you're confident, proceed. If unsure, continue refining.`;\n }\n\n /**\n * Get summary of file edits for debugging\n */\n getEditSummary(): { totalFiles: number; totalEdits: number; topFiles: Array<{ path: string; count: number }> } {\n const entries = Array.from(this.fileEdits.entries());\n const totalEdits = entries.reduce((sum, [, record]) => sum + record.editCount, 0);\n const topFiles = entries\n .sort((a, b) => b[1].editCount - a[1].editCount)\n .slice(0, 5)\n .map(([path, record]) => ({ path, count: record.editCount }));\n\n return {\n totalFiles: entries.length,\n totalEdits,\n topFiles,\n };\n }\n\n /**\n * Update configuration\n */\n setConfig(config: Partial<SelfVerifyConfig>): void {\n this.config = { ...this.config, ...config };\n }\n\n /**\n * Get current configuration\n */\n getConfig(): SelfVerifyConfig {\n return { ...this.config };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"self-verify.js","names":[],"sources":["../../../../src/agent/middleware/self-verify.ts"],"sourcesContent":["/**\n * Self-Verification Middleware\n *\n * Implements the \"Build & Self-Verify\" pattern from harness engineering:\n * - Intercepts agent before completion to remind verification\n * - Tracks file edits to detect potential issues\n * - Injects verification reminders into context\n *\n * Inspired by: https://blog.langchain.com/improving-deep-agents-with-harness-engineering/\n */\n\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('SelfVerifyMiddleware');\n\nexport interface FileEditRecord {\n path: string;\n editCount: number;\n lastEditTime: number;\n operations: string[]; // 'write', 'edit', 'command' (for build commands)\n}\n\nexport interface SelfVerifyConfig {\n /** Max edits to same file before warning (default: 5) */\n maxEditsPerFile: number;\n /** Enable pre-completion verification check (default: true) */\n enablePreCompletionCheck: boolean;\n /** Min turns before triggering verification reminder (default: 3) */\n minTurnsForVerification: number;\n /** Reset counters on successful verification (default: true) */\n resetOnVerification: boolean;\n}\n\nconst DEFAULT_CONFIG: SelfVerifyConfig = {\n maxEditsPerFile: 5,\n enablePreCompletionCheck: true,\n minTurnsForVerification: 3,\n resetOnVerification: true,\n};\n\nexport interface VerificationOutcome {\n isError?: boolean;\n result?: unknown;\n}\n\nexport interface VerificationRecord {\n toolName: string;\n command?: string;\n success: boolean;\n exitCode?: number | null;\n status?: string;\n timedOut?: boolean;\n}\n\nexport interface SelfVerifyState {\n hasUnverifiedEdits: boolean;\n changedFiles: string[];\n diffReviewed: boolean;\n verificationAttempted: boolean;\n lastVerificationFailed: boolean;\n lastVerification?: VerificationRecord;\n lastMutationTool?: string;\n}\n\nexport type SelfVerifyAgentKind = 'coder' | 'data' | 'writer' | 'researcher' | 'creative' | 'generic';\n\ninterface SelfVerifyTrackerState {\n fileEdits: Map<string, FileEditRecord>;\n turnCount: number;\n verificationRequested: boolean;\n hasUnverifiedEdits: boolean;\n diffReviewed: boolean;\n lastVerification: VerificationRecord | undefined;\n lastMutationTool: string | undefined;\n}\n\nfunction createTrackerState(): SelfVerifyTrackerState {\n return {\n fileEdits: new Map(),\n turnCount: 0,\n verificationRequested: false,\n hasUnverifiedEdits: false,\n diffReviewed: false,\n lastVerification: undefined,\n lastMutationTool: undefined,\n };\n}\n\n/**\n * Tracks file modifications and provides verification guidance\n */\nexport class SelfVerifyMiddleware {\n private readonly states = new Map<string, SelfVerifyTrackerState>();\n private config: SelfVerifyConfig;\n\n constructor(config: Partial<SelfVerifyConfig> = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n private getState(sessionKey?: string): SelfVerifyTrackerState {\n const key = sessionKey ?? 'default';\n let state = this.states.get(key);\n if (!state) {\n state = createTrackerState();\n this.states.set(key, state);\n }\n return state;\n }\n\n /**\n * Record a file edit operation\n */\n recordEdit(filePath: string, operation: 'write' | 'edit' | 'command', sessionKey?: string): void {\n const state = this.getState(sessionKey);\n const now = Date.now();\n const existing = state.fileEdits.get(filePath);\n\n if (existing) {\n existing.editCount++;\n existing.lastEditTime = now;\n if (!existing.operations.includes(operation)) {\n existing.operations.push(operation);\n }\n } else {\n state.fileEdits.set(filePath, {\n path: filePath,\n editCount: 1,\n lastEditTime: now,\n operations: [operation],\n });\n }\n\n state.hasUnverifiedEdits = true;\n state.diffReviewed = false;\n state.lastMutationTool = operation;\n log.debug({ filePath, editCount: this.getEditCount(filePath, sessionKey), operation }, 'File edit recorded');\n }\n\n recordVerification(toolName: string, args?: unknown, outcome?: VerificationOutcome, sessionKey?: string): void {\n const state = this.getState(sessionKey);\n const name = toolName.toLowerCase();\n if (name === 'exec_command') {\n const command = this.extractExecCommand(args, outcome?.result);\n const success = this.isSuccessfulExecVerification(outcome);\n const details = this.extractResultDetails(outcome?.result);\n\n if (success && this.isDiffReviewCommand(command)) {\n state.diffReviewed = true;\n return;\n }\n\n if (!this.isVerificationCommand(command)) {\n return;\n }\n\n const rawExitCode = details?.exitCode;\n let exitCode: number | null | undefined;\n if (typeof rawExitCode === 'number') {\n exitCode = rawExitCode;\n } else if (rawExitCode === null) {\n exitCode = null;\n }\n state.lastVerification = {\n toolName,\n command,\n success,\n exitCode,\n status: typeof details?.status === 'string' ? details.status : undefined,\n timedOut: details?.timedOut === true,\n };\n\n if (success) {\n state.hasUnverifiedEdits = false;\n }\n return;\n }\n if (name.includes('test') || name.includes('verify') || name.includes('lint')) {\n state.hasUnverifiedEdits = false;\n state.lastVerification = { toolName, success: true };\n }\n }\n\n getVerificationState(sessionKey?: string): SelfVerifyState {\n const state = this.getState(sessionKey);\n const changedFiles = Array.from(state.fileEdits.keys()).sort();\n const lastVerification = state.lastVerification\n ? { ...state.lastVerification }\n : undefined;\n return {\n hasUnverifiedEdits: state.hasUnverifiedEdits,\n changedFiles,\n diffReviewed: state.diffReviewed,\n verificationAttempted: Boolean(lastVerification),\n lastVerificationFailed: Boolean(lastVerification && !lastVerification.success),\n lastVerification,\n lastMutationTool: state.lastMutationTool,\n };\n }\n\n getPendingVerificationContext(sessionKey?: string, agentId?: string): string {\n const state = this.getState(sessionKey);\n if (!this.config.enablePreCompletionCheck || !state.hasUnverifiedEdits) {\n return '';\n }\n return this.buildPendingVerificationReminder(sessionKey, agentId);\n }\n\n private extractExecCommand(args?: unknown, result?: unknown): string {\n if (args && typeof args === 'object') {\n const rec = args as { cmd?: unknown; command?: unknown };\n if (typeof rec.cmd === 'string') return rec.cmd.toLowerCase();\n if (typeof rec.command === 'string') return rec.command.toLowerCase();\n }\n\n const details = this.extractResultDetails(result);\n if (details) {\n const command = details.command;\n if (typeof command === 'string') return command.toLowerCase();\n }\n return '';\n }\n\n private extractResultDetails(result: unknown): Record<string, unknown> | null {\n if (!result || typeof result !== 'object') return null;\n const details = (result as { details?: unknown }).details;\n return details && typeof details === 'object'\n ? (details as Record<string, unknown>)\n : null;\n }\n\n private isSuccessfulExecVerification(outcome?: VerificationOutcome): boolean {\n if (!outcome || outcome.isError) return false;\n const details = this.extractResultDetails(outcome.result);\n if (!details) return false;\n if (details.timedOut === true) return false;\n if (details.status === 'success') return true;\n return details.exitCode === 0;\n }\n\n private isVerificationCommand(command: string): boolean {\n return /\\b(test|vitest|jest|mocha|ava|playwright|typecheck|type-check|tsc|build|lint)\\b/.test(command);\n }\n\n private isDiffReviewCommand(command: string): boolean {\n return /\\bgit\\s+(diff|status)\\b/.test(command);\n }\n\n /**\n * Get edit count for a specific file\n */\n getEditCount(filePath: string, sessionKey?: string): number {\n return this.getState(sessionKey).fileEdits.get(filePath)?.editCount || 0;\n }\n\n /**\n * Check if any file has excessive edits (potential doom loop)\n */\n hasExcessiveEdits(sessionKey?: string): { file: string; count: number } | null {\n for (const [path, record] of this.getState(sessionKey).fileEdits.entries()) {\n if (record.editCount >= this.config.maxEditsPerFile) {\n return { file: path, count: record.editCount };\n }\n }\n return null;\n }\n\n /**\n * Increment turn counter\n */\n onTurnStart(sessionKey?: string): void {\n const state = this.getState(sessionKey);\n state.turnCount++;\n state.verificationRequested = false;\n }\n\n /**\n * Reset all tracking (e.g., after successful task completion)\n */\n reset(sessionKey?: string): void {\n if (sessionKey) {\n this.states.set(sessionKey, createTrackerState());\n } else {\n this.states.clear();\n }\n log.debug('Self-verify middleware reset');\n }\n\n consumePostEditReminder(sessionKey?: string): string {\n const state = this.getState(sessionKey);\n if (!this.config.enablePreCompletionCheck || !state.hasUnverifiedEdits) {\n return '';\n }\n if (state.verificationRequested) {\n return '';\n }\n state.verificationRequested = true;\n return this.buildPendingVerificationReminder(sessionKey);\n }\n\n /**\n * Get context injection for system prompt\n * Adds verification guidance based on current state\n */\n getContextInjection(sessionKey?: string, agentId?: string): string {\n const sections: string[] = [];\n\n // Add workflow guidance\n sections.push(this.buildWorkflowGuidance());\n\n // Add excessive edit warning if needed\n const excessive = this.hasExcessiveEdits(sessionKey);\n if (excessive) {\n sections.push(this.buildExcessiveEditWarning(excessive.file, excessive.count));\n }\n\n // Add pre-completion check reminder for long sessions\n if (this.shouldPromptForVerification(sessionKey)) {\n sections.push(this.buildPreCompletionReminder(sessionKey));\n }\n\n const pending = this.getPendingVerificationContext(sessionKey, agentId);\n if (pending) {\n sections.push(pending);\n }\n\n return sections.filter(Boolean).join('\\n\\n');\n }\n\n /**\n * Check if we should inject verification reminder\n */\n private shouldPromptForVerification(sessionKey?: string): boolean {\n const state = this.getState(sessionKey);\n if (!this.config.enablePreCompletionCheck) return false;\n if (state.turnCount < this.config.minTurnsForVerification) return false;\n if (state.verificationRequested) return false;\n return true;\n }\n\n /**\n * Mark verification as requested (to avoid spam)\n */\n markVerificationRequested(sessionKey?: string): void {\n this.getState(sessionKey).verificationRequested = true;\n }\n\n /**\n * Build workflow guidance section\n */\n private buildWorkflowGuidance(): string {\n return `## Problem Solving Workflow\n\nFollow this iterative process for all tasks:\n\n1. **Plan**: Understand the task, read relevant files, and create a plan\n2. **Build**: Implement your solution with verification in mind\n3. **Verify**: Test your work, run checks, compare against requirements\n4. **Fix**: If issues found, analyze and fix them\n\n**Important**: Before declaring a task complete:\n- Re-read the original requirements\n- Verify your solution meets ALL requirements\n- Run any available tests or validation\n- Check for edge cases\n\nDo not skip verification. Incomplete verification leads to incorrect solutions.`;\n }\n\n private classifyAgent(agentId?: string): SelfVerifyAgentKind {\n const id = agentId?.trim().toLowerCase() ?? '';\n if (!id) return 'generic';\n if (id.includes('coder') || id.includes('code') || id.includes('developer')) return 'coder';\n if (id.includes('data') || id.includes('analyst') || id.includes('analysis')) return 'data';\n if (id.includes('writer') || id.includes('writing')) return 'writer';\n if (id.includes('research')) return 'researcher';\n if (id.includes('creative') || id.includes('design')) return 'creative';\n return 'generic';\n }\n\n private buildPendingVerificationReminder(sessionKey?: string, agentId?: string): string {\n const state = this.getVerificationState(sessionKey);\n const kind = this.classifyAgent(agentId);\n const files = state.changedFiles.length > 0\n ? state.changedFiles.slice(0, 12).join(', ')\n : '(unknown files)';\n const extraFiles = state.changedFiles.length > 12\n ? `, and ${state.changedFiles.length - 12} more`\n : '';\n const profile = this.verificationProfile(kind);\n const lines = [\n `## ${profile.title}`,\n profile.checkLine,\n `${profile.changedLabel}: ${files}${extraFiles}.`,\n state.diffReviewed\n ? 'Diff review: completed with git diff/status.'\n : 'Diff review: not recorded yet.',\n ];\n\n if (state.lastVerification) {\n const command = state.lastVerification.command\n ? ` (${state.lastVerification.command})`\n : '';\n lines.push(\n state.lastVerification.success\n ? `Last verification: passed${command}.`\n : `Last verification: failed${command}.`,\n );\n } else {\n lines.push('Last verification: none recorded.');\n }\n\n lines.push(profile.finalGuidance);\n return lines.join('\\n');\n }\n\n private verificationProfile(kind: SelfVerifyAgentKind): {\n title: string;\n checkLine: string;\n changedLabel: string;\n finalGuidance: string;\n } {\n switch (kind) {\n case 'coder':\n return {\n title: 'Coder Verification State',\n checkLine: 'Coder check: source files changed.',\n changedLabel: 'Changed source files',\n finalGuidance:\n 'Before final response, inspect the diff and run the smallest meaningful code verification, such as a targeted test, typecheck, lint, or build. If verification cannot run, final response must state why and name the remaining risk.',\n };\n case 'data':\n return {\n title: 'Data Verification State',\n checkLine: 'Data check: analysis files or outputs changed.',\n changedLabel: 'Changed files or artifacts',\n finalGuidance:\n 'Before final response, inspect the diff or generated outputs and run the smallest meaningful data validation, such as rerunning the analysis, checking row counts, schemas, filters, joins, units, or output files. If validation cannot run, final response must state why and name the remaining risk.',\n };\n case 'writer':\n return {\n title: 'Writing Review State',\n checkLine: 'Writing check: draft or document files changed.',\n changedLabel: 'Changed files or artifacts',\n finalGuidance:\n 'Before final response, inspect the changed artifact for requested content, structure, formatting, placeholders, and obvious inconsistencies. If a review cannot be completed, final response must state why and name the remaining risk.',\n };\n case 'researcher':\n return {\n title: 'Research Review State',\n checkLine: 'Research check: research notes or report files changed.',\n changedLabel: 'Changed files or artifacts',\n finalGuidance:\n 'Before final response, inspect the changed artifact and verify citations, source-backed claims, unresolved uncertainty, and formatting. If source or artifact review cannot be completed, final response must state why and name the remaining risk.',\n };\n case 'creative':\n return {\n title: 'Creative Review State',\n checkLine: 'Creative check: creative artifact files changed.',\n changedLabel: 'Changed files or artifacts',\n finalGuidance:\n 'Before final response, inspect the changed artifact against the brief, requested format, visual/text quality, and any saved outputs. If review cannot be completed, final response must state why and name the remaining risk.',\n };\n case 'generic':\n default:\n return {\n title: 'Workspace Verification State',\n checkLine: 'Workspace check: files changed.',\n changedLabel: 'Changed files or artifacts',\n finalGuidance:\n 'Before final response, inspect the changed files and run the smallest meaningful verification for this task. If verification cannot run, final response must state why and name the remaining risk.',\n };\n }\n }\n\n /**\n * Build warning for excessive file edits (doom loop detection)\n */\n private buildExcessiveEditWarning(filePath: string, count: number): string {\n return `⚠️ **Pattern Alert**: You have edited \"${filePath}\" ${count} times.\n\nThis may indicate:\n- You're fixing symptoms rather than root causes\n- The approach needs reconsideration\n- Requirements may be unclear\n\n**Recommendation**: \n- Step back and re-read the original task\n- Consider if there's a better approach\n- Verify you understand the requirements correctly`;\n }\n\n /**\n * Build pre-completion verification reminder\n */\n private buildPreCompletionReminder(sessionKey?: string): string {\n const state = this.getState(sessionKey);\n this.markVerificationRequested(sessionKey);\n return `⏳ **Verification Check**: You've made ${state.turnCount} turns on this task.\n\nBefore completing:\n1. ✅ Verify your solution matches the original requirements\n2. ✅ Run tests or validation if available\n3. ✅ Check edge cases and error handling\n4. ✅ Review your changes one final time\n\nIf you're confident, proceed. If unsure, continue refining.`;\n }\n\n /**\n * Get summary of file edits for debugging\n */\n getEditSummary(sessionKey?: string): { totalFiles: number; totalEdits: number; topFiles: Array<{ path: string; count: number }> } {\n const entries = Array.from(this.getState(sessionKey).fileEdits.entries());\n const totalEdits = entries.reduce((sum, [, record]) => sum + record.editCount, 0);\n const topFiles = entries\n .sort((a, b) => b[1].editCount - a[1].editCount)\n .slice(0, 5)\n .map(([path, record]) => ({ path, count: record.editCount }));\n\n return {\n totalFiles: entries.length,\n totalEdits,\n topFiles,\n };\n }\n\n /**\n * Update configuration\n */\n setConfig(config: Partial<SelfVerifyConfig>): void {\n this.config = { ...this.config, ...config };\n }\n\n /**\n * Get current configuration\n */\n getConfig(): SelfVerifyConfig {\n return { ...this.config };\n }\n}\n"],"mappings":"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"}
@@ -174,6 +174,40 @@ function appendTextToToolResult(result, text) {
174
174
  }
175
175
  return result;
176
176
  }
177
+ function readRecord(value) {
178
+ return value && typeof value === "object" ? value : null;
179
+ }
180
+ function readToolResultDetails(result) {
181
+ const rec = readRecord(result);
182
+ if (!rec) return null;
183
+ return readRecord(rec.details);
184
+ }
185
+ function readStringArray(value) {
186
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
187
+ }
188
+ function readChangedPathsFromToolEnd(toolName, args, result) {
189
+ const name = toolName.toLowerCase();
190
+ const details = readToolResultDetails(result);
191
+ if (name === "apply_patch") {
192
+ const files = readStringArray(details?.files);
193
+ if (files.length > 0) return files;
194
+ return (Array.isArray(details?.changes) ? details.changes : []).flatMap((change) => {
195
+ const rec = readRecord(change);
196
+ if (!rec) return [];
197
+ return readStringArray([rec.moveTo, rec.path]);
198
+ });
199
+ }
200
+ if (name === "write_file") {
201
+ if (typeof details?.size !== "number") return [];
202
+ const rec = readRecord(args);
203
+ return typeof rec?.path === "string" ? [rec.path] : [];
204
+ }
205
+ if (name.includes("write") || name.includes("edit")) {
206
+ const rec = readRecord(args);
207
+ return typeof rec?.path === "string" ? [rec.path] : [];
208
+ }
209
+ return [];
210
+ }
177
211
  function installToolChainListener(bus, toolChainTracker) {
178
212
  bus.on("turn_start", (_event, context) => {
179
213
  toolChainTracker.startChain(context.sessionKey);
@@ -232,24 +266,32 @@ function installErrorTrackingListener(bus, deps) {
232
266
  });
233
267
  }
234
268
  function installSelfVerifyListener(bus, selfVerifyMiddleware) {
235
- bus.on("tool_execution_start", (event) => {
236
- const e = event;
237
- const args = e.args;
238
- const toolName = e.toolName;
239
- if (!toolName || !args) return;
240
- const name = toolName.toLowerCase();
241
- if (name.includes("write") && args.path) selfVerifyMiddleware.recordEdit(String(args.path), "write");
242
- else if (name.includes("edit") && args.path) selfVerifyMiddleware.recordEdit(String(args.path), "edit");
243
- else selfVerifyMiddleware.recordVerification(toolName, args);
244
- });
245
- bus.on("tool_execution_end", (event) => {
269
+ bus.on("tool_execution_end", (event, context) => {
246
270
  const e = event;
247
271
  const name = e.toolName?.toLowerCase() ?? "";
248
- if (!name.includes("write") && !name.includes("edit")) return;
249
- e.result = appendTextToToolResult(e.result, selfVerifyMiddleware.consumePostEditReminder());
272
+ const args = e.args;
273
+ if (name === "exec_command") {
274
+ selfVerifyMiddleware.recordVerification(e.toolName, args, {
275
+ isError: e.isError,
276
+ result: e.result
277
+ }, context.sessionKey);
278
+ return;
279
+ }
280
+ if (!e.isError) {
281
+ const changedPaths = readChangedPathsFromToolEnd(e.toolName, args, e.result);
282
+ for (const path of changedPaths) selfVerifyMiddleware.recordEdit(path, name.includes("write") ? "write" : "edit", context.sessionKey);
283
+ if (changedPaths.length > 0) {
284
+ e.result = appendTextToToolResult(e.result, selfVerifyMiddleware.consumePostEditReminder(context.sessionKey));
285
+ return;
286
+ }
287
+ selfVerifyMiddleware.recordVerification(e.toolName, args, {
288
+ isError: e.isError,
289
+ result: e.result
290
+ }, context.sessionKey);
291
+ }
250
292
  });
251
- bus.on("turn_start", () => {
252
- selfVerifyMiddleware.onTurnStart();
293
+ bus.on("turn_start", (_event, context) => {
294
+ selfVerifyMiddleware.onTurnStart(context.sessionKey);
253
295
  });
254
296
  }
255
297
  /**