@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
@@ -1 +1 @@
1
- {"version":3,"file":"progress.js","names":[],"sources":["../../../../src/agent/lifecycle/progress.ts"],"sourcesContent":["/**\n * Progress Feedback Manager\n * \n * Manages real-time progress feedback for long-running agent tasks.\n * Provides:\n * - Tool/end notifications execution start\n * - Thinking indicators\n * - Progress stage updates\n * - Stream integration for real-time display\n */\n\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('ProgressFeedback');\n\n// Progress stages for different operations\nexport type ProgressStage = \n | 'thinking' // AI is thinking/reasoning\n | 'searching' // Web search\n | 'reading' // Reading files\n | 'writing' // Writing files \n | 'executing' // Running shell commands\n | 'analyzing' // Analyzing data\n | 'idle'; // No active operation\n\nexport interface ProgressUpdate {\n stage: ProgressStage;\n detail?: string;\n toolName?: string;\n toolArgs?: Record<string, unknown>;\n timestamp: number;\n}\n\nexport interface ProgressFeedbackConfig {\n // Feedback level: minimal (errors only), normal (key events), verbose (all updates)\n level: 'minimal' | 'normal' | 'verbose';\n // Whether to show thinking indicators\n showThinking: boolean;\n // Whether to stream tool progress updates\n streamToolProgress: boolean;\n // Whether to send periodic heartbeats for long tasks\n heartbeatEnabled: boolean;\n // Heartbeat interval in ms\n heartbeatIntervalMs: number;\n // Threshold for long-running task in ms\n longTaskThresholdMs: number;\n // Enhanced feedback options\n showErrorAccumulation: boolean; // Show tool error accumulation (default: true)\n showRequestLimitWarning: boolean; // Show request limit warnings (default: true)\n}\n\nconst DEFAULT_CONFIG: ProgressFeedbackConfig = {\n level: 'normal',\n showThinking: true,\n streamToolProgress: true,\n heartbeatEnabled: true,\n heartbeatIntervalMs: 20000, // 20 seconds\n longTaskThresholdMs: 30000, // 30 seconds\n showErrorAccumulation: true,\n showRequestLimitWarning: true,\n};\n\n// Tool category mapping for better feedback\nconst TOOL_STAGE_MAP: Record<string, ProgressStage> = {\n // Reading tools\n 'read': 'reading',\n 'read_file': 'reading',\n 'read_multiple': 'reading',\n 'glob': 'reading',\n 'find': 'reading',\n 'list_dir': 'reading',\n 'ls': 'reading',\n 'memory_get': 'reading',\n 'curated_memory': 'writing',\n\n // Searching tools\n 'grep': 'searching',\n 'web_search': 'searching',\n 'search': 'searching',\n 'memory_search': 'searching',\n 'session_search': 'searching',\n 'find_files': 'searching',\n \n // Web fetching\n 'web_fetch': 'reading',\n 'fetch': 'reading',\n 'curl': 'reading',\n 'wget': 'reading',\n \n // Writing tools\n 'write': 'writing',\n 'write_file': 'writing',\n 'write_multiple': 'writing',\n 'edit': 'writing',\n 'patch': 'writing',\n 'create': 'writing',\n 'mkdir': 'writing',\n 'delete': 'writing',\n 'remove': 'writing',\n \n // Executing tools\n 'bash': 'executing',\n 'shell': 'executing',\n 'exec': 'executing',\n 'run': 'executing',\n 'command': 'executing',\n 'node': 'executing',\n 'python': 'executing',\n 'npm': 'executing',\n 'pnpm': 'executing',\n 'git': 'executing',\n 'docker': 'executing',\n 'kubectl': 'executing',\n \n // Message/communication\n 'message': 'idle',\n 'send': 'idle',\n 'reply': 'idle',\n \n // Analysis tools\n 'analyze': 'analyzing',\n 'compare': 'analyzing',\n 'diff': 'analyzing',\n 'summarize': 'analyzing',\n};\n\n// Emoji mapping for stages\nconst STAGE_EMOJI: Record<ProgressStage, string> = {\n 'thinking': '🤔',\n 'searching': '🔍',\n 'reading': '📖',\n 'writing': '✍️',\n 'executing': '⚙️',\n 'analyzing': '📊',\n 'idle': '💬',\n};\n\n// Stage labels in English\nconst STAGE_LABEL: Record<ProgressStage, string> = {\n 'thinking': 'Thinking',\n 'searching': 'Searching',\n 'reading': 'Reading',\n 'writing': 'Writing',\n 'executing': 'Executing',\n 'analyzing': 'Analyzing',\n 'idle': 'Ready',\n};\n\nexport interface ProgressMessage {\n type: 'start' | 'update' | 'complete' | 'error' | 'warning' | 'thinking';\n stage: ProgressStage;\n message: string;\n detail?: string;\n toolName?: string;\n toolArgs?: Record<string, unknown>;\n}\n\nexport interface ProgressCallbacks {\n onProgress?: (msg: ProgressMessage) => void;\n onStreamStart?: (toolName: string, toolArgs: Record<string, unknown>) => void;\n onStreamUpdate?: (toolName: string, partialResult: string) => void;\n onStreamEnd?: (toolName: string, result: string, isError: boolean) => void;\n onThinking?: (thinking: string) => void;\n onHeartbeat?: (elapsedMs: number, currentStage: ProgressStage) => void;\n}\n\nexport class ProgressFeedbackManager {\n private config: ProgressFeedbackConfig;\n private callbacks: ProgressCallbacks = {};\n private currentStage: ProgressStage = 'idle';\n private currentTool: string | null = null;\n private taskStartTime: number = 0;\n private heartbeatTimer?: ReturnType<typeof setInterval>;\n private streamActive: boolean = false;\n\n constructor(config: Partial<ProgressFeedbackConfig> = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n setCallbacks(callbacks: ProgressCallbacks): void {\n this.callbacks = { ...this.callbacks, ...callbacks };\n }\n\n startTask(): void {\n this.taskStartTime = Date.now();\n \n if (this.config.heartbeatEnabled) {\n this.startHeartbeat();\n }\n }\n\n endTask(): void {\n this.stopHeartbeat();\n this.currentStage = 'idle';\n this.currentTool = null;\n this.taskStartTime = 0;\n this.streamActive = false;\n }\n\n private startHeartbeat(): void {\n this.stopHeartbeat();\n this.heartbeatTimer = setInterval(() => {\n const elapsed = Date.now() - this.taskStartTime;\n if (elapsed >= this.config.longTaskThresholdMs) {\n this.callbacks.onHeartbeat?.(elapsed, this.currentStage);\n }\n }, this.config.heartbeatIntervalMs);\n }\n\n private stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = undefined;\n }\n }\n\n private mapToolToStage(toolName: string): ProgressStage {\n const normalizedName = toolName.toLowerCase();\n const normalizedNoUnderscore = normalizedName.replace(/_/g, '');\n \n // Try both versions: with and without underscores\n const toCheck = [normalizedName, normalizedNoUnderscore];\n \n // Sort keys by length (longest first) to match more specific keys first\n const sortedKeys = Object.keys(TOOL_STAGE_MAP).sort((a, b) => b.length - a.length);\n \n for (const name of toCheck) {\n for (const key of sortedKeys) {\n if (name.includes(key)) {\n return TOOL_STAGE_MAP[key];\n }\n }\n }\n return 'executing';\n }\n\n private formatToolName(toolName: string): string {\n // Convert snake_case to Title Case\n return toolName\n .replace(/_/g, ' ')\n .replace(/\\b\\w/g, c => c.toUpperCase());\n }\n\n // Handle tool execution start\n onToolStart(toolName: string, toolArgs: Record<string, unknown> = {}): void {\n if (this.config.level === 'minimal') return;\n\n this.currentTool = toolName;\n this.currentStage = this.mapToolToStage(toolName);\n this.streamActive = true;\n\n const formattedName = this.formatToolName(toolName);\n const emoji = STAGE_EMOJI[this.currentStage];\n const label = STAGE_LABEL[this.currentStage];\n\n // Build detail message\n let detail = '';\n if (toolArgs.path) {\n detail = `📁 ${toolArgs.path}`;\n } else if (toolArgs.query) {\n detail = `🔎 ${toolArgs.query}`;\n } else if (toolArgs.command) {\n const cmd = String(toolArgs.command);\n detail = `⚡ ${cmd.length > 50 ? cmd.slice(0, 50) + '...' : cmd}`;\n }\n\n const message: ProgressMessage = {\n type: 'start',\n stage: this.currentStage,\n message: `${emoji} ${label}: ${formattedName}`,\n detail: detail || undefined,\n toolName,\n toolArgs,\n };\n\n if (this.config.level === 'verbose' || this.config.streamToolProgress) {\n this.callbacks.onProgress?.(message);\n }\n this.callbacks.onStreamStart?.(toolName, toolArgs);\n\n log.debug({ toolName, stage: this.currentStage }, 'Tool execution started');\n }\n\n // Handle tool execution update (streaming)\n onToolUpdate(toolName: string, partialResult: unknown): void {\n if (this.config.level === 'minimal') return;\n if (!this.streamActive || !this.config.streamToolProgress) return;\n\n // Format partial result for display\n let displayResult = '';\n if (typeof partialResult === 'string') {\n displayResult = partialResult.slice(0, 200); // Truncate for display\n } else if (partialResult && typeof partialResult === 'object') {\n const result = partialResult as Record<string, unknown>;\n if (result.output) {\n displayResult = String(result.output).slice(0, 200);\n } else if (result.content) {\n displayResult = String(result.content).slice(0, 200);\n } else if (result.text) {\n displayResult = String(result.text).slice(0, 200);\n }\n }\n\n this.callbacks.onStreamUpdate?.(toolName, displayResult);\n }\n\n // Handle tool execution end\n onToolEnd(toolName: string, result: unknown, isError: boolean = false): void {\n if (this.config.level === 'minimal' && !isError) return;\n\n const wasActive = this.streamActive;\n this.streamActive = false;\n\n // Extract result summary\n let resultSummary = '';\n if (result && typeof result === 'object') {\n const res = result as Record<string, unknown>;\n if (res.content && Array.isArray(res.content)) {\n const textContent = res.content.find((c: unknown) => \n typeof c === 'object' && c !== null && (c as Record<string, unknown>).type === 'text'\n );\n if (textContent) {\n resultSummary = String((textContent as Record<string, unknown>).text || '').slice(0, 100);\n }\n }\n }\n\n const message: ProgressMessage = {\n type: isError ? 'error' : 'complete',\n stage: this.currentStage,\n message: isError \n ? `❌ Tool failed: ${this.formatToolName(toolName)}`\n : `✅ Done: ${this.formatToolName(toolName)}`,\n toolName,\n };\n\n if (wasActive) {\n this.callbacks.onStreamEnd?.(toolName, resultSummary, isError);\n }\n \n if (isError || this.config.level !== 'minimal') {\n this.callbacks.onProgress?.(message);\n }\n\n // Reset current tool if it matches\n if (this.currentTool === toolName) {\n this.currentTool = null;\n this.currentStage = 'idle';\n }\n\n log.debug({ toolName, isError }, 'Tool execution ended');\n }\n\n // Handle thinking/reasoning\n onThinking(thinking: string): void {\n if (!this.config.showThinking || this.config.level === 'minimal') return;\n\n const displayThinking = thinking.length > 100 \n ? thinking.slice(0, 100) + '...'\n : thinking;\n\n this.callbacks.onThinking?.(displayThinking);\n }\n\n // Handle turn start\n onTurnStart(): void {\n if (this.config.level === 'minimal') return;\n \n this.currentStage = 'thinking';\n this.taskStartTime = Date.now();\n \n if (this.config.heartbeatEnabled && !this.heartbeatTimer) {\n this.startHeartbeat();\n }\n }\n\n // Handle message/response start\n onMessageStart(): void {\n // Could be used to show \"AI is typing...\" indicator\n }\n\n // Handle message/response update (streaming tokens)\n onMessageUpdate(_content: string): void {\n // Could be used to show live response\n }\n\n // Handle agent end\n onAgentEnd(): void {\n this.endTask();\n }\n\n // Get current state\n getCurrentState(): { stage: ProgressStage; tool: string | null; elapsedMs: number } {\n return {\n stage: this.currentStage,\n tool: this.currentTool,\n elapsedMs: this.taskStartTime ? Date.now() - this.taskStartTime : 0,\n };\n }\n\n // Update config\n setConfig(config: Partial<ProgressFeedbackConfig>): void {\n this.config = { ...this.config, ...config };\n }\n\n // Get stage emoji\n getStageEmoji(stage: ProgressStage): string {\n return STAGE_EMOJI[stage] || '💬';\n }\n\n // Get stage label\n getStageLabel(stage: ProgressStage): string {\n return STAGE_LABEL[stage] || 'Unknown';\n }\n\n /**\n * Notify about accumulated tool errors\n * Enhanced feedback for tool failures\n */\n onToolErrorAccumulated(\n toolName: string,\n failureCount: number,\n maxFailures: number,\n remainingAttempts?: number\n ): void {\n if (!this.config.showErrorAccumulation) return;\n if (this.config.level === 'minimal') return;\n\n const remaining = remainingAttempts ?? (maxFailures - failureCount);\n const percentage = (failureCount / maxFailures) * 100;\n \n let message: string;\n let type: 'warning' | 'error' = percentage >= 100 ? 'error' : 'warning';\n\n if (percentage >= 100) {\n message = `❌ ${this.formatToolName(toolName)} has reached maximum failures (${maxFailures}/${maxFailures})`;\n } else if (percentage >= 50) {\n message = `⚠️ ${this.formatToolName(toolName)} failed ${failureCount}/${maxFailures} times`;\n } else {\n message = `⚠️ ${this.formatToolName(toolName)} failed ${failureCount} time${failureCount > 1 ? 's' : ''}`;\n }\n\n const progressMsg: ProgressMessage = {\n type,\n stage: 'executing',\n message,\n detail: remaining > 0 \n ? `Remaining attempts: ${remaining}` \n : 'Stopping tool execution',\n toolName,\n };\n\n this.callbacks.onProgress?.(progressMsg);\n\n const atCap = maxFailures > 0 && failureCount >= maxFailures;\n const summary = atCap\n ? `Tool \"${toolName}\" reached max failures (${failureCount}/${maxFailures})`\n : `Tool \"${toolName}\" failures ${failureCount}/${maxFailures} (${remaining} attempt(s) left)`;\n if (atCap) {\n log.error({ toolName, failureCount, maxFailures, remaining }, summary);\n } else {\n log.warn({ toolName, failureCount, maxFailures, remaining }, summary);\n }\n }\n\n /**\n * Notify about request limit status\n * Enhanced feedback for approaching/ reaching request limits\n */\n onRequestLimitStatus(\n count: number,\n limit: number,\n remaining: number,\n isWarning: boolean,\n shouldStop: boolean\n ): void {\n if (!this.config.showRequestLimitWarning) return;\n if (this.config.level === 'minimal') return;\n if (!isWarning && !shouldStop) return;\n\n const percentage = (count / limit) * 100;\n \n let message: string;\n let type: 'warning' | 'error' = shouldStop ? 'error' : 'warning';\n\n if (shouldStop) {\n message = `❌ Request limit reached (${count}/${limit})`;\n } else if (percentage >= 90) {\n message = `⚠️ Request limit warning (${count}/${limit})`;\n } else {\n message = `⚠️ Approaching request limit (${count}/${limit})`;\n }\n\n const progressMsg: ProgressMessage = {\n type,\n stage: 'thinking',\n message,\n detail: remaining > 0 \n ? `Remaining requests: ${remaining}` \n : 'Stopping execution',\n };\n\n this.callbacks.onProgress?.(progressMsg);\n\n const summary = shouldStop\n ? `LLM request limit reached (${count}/${limit}); stopping turn`\n : `LLM request budget ${count}/${limit} (${remaining} left)`;\n if (shouldStop) {\n log.error({ count, limit, remaining, shouldStop }, summary);\n } else {\n log.warn({ count, limit, remaining, shouldStop }, summary);\n }\n }\n}\n\n// Singleton instance with default config\nexport const progressFeedbackManager = new ProgressFeedbackManager();\n\n// Utility function to format progress messages for Telegram\nexport function formatProgressMessage(msg: ProgressMessage, parseMode: 'Markdown' | 'HTML' = 'HTML'): string {\n const emoji = STAGE_EMOJI[msg.stage];\n \n if (parseMode === 'HTML') {\n let html = `<b>${emoji} ${msg.message}</b>`;\n if (msg.detail) {\n html += `\\n${msg.detail}`;\n }\n return html;\n } else {\n let md = `**${emoji} ${msg.message}**`;\n if (msg.detail) {\n md += `\\n${msg.detail}`;\n }\n return md;\n }\n}\n\n// Utility function to format heartbeat message\nexport function formatHeartbeatMessage(elapsedMs: number, stage: ProgressStage, detail?: string): string {\n const seconds = Math.floor(elapsedMs / 1000);\n const emoji = STAGE_EMOJI[stage];\n const label = STAGE_LABEL[stage];\n \n let message = `${emoji} Still ${label.toLowerCase()}...`;\n if (detail) {\n message += `\\n${detail}`;\n }\n message += `\\n⏱️ ${seconds}s elapsed`;\n \n return message;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"progress.js","names":[],"sources":["../../../../src/agent/lifecycle/progress.ts"],"sourcesContent":["/**\n * Progress Feedback Manager\n * \n * Manages real-time progress feedback for long-running agent tasks.\n * Provides:\n * - Tool/end notifications execution start\n * - Thinking indicators\n * - Progress stage updates\n * - Stream integration for real-time display\n */\n\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('ProgressFeedback');\n\n// Progress stages for different operations\nexport type ProgressStage = \n | 'thinking' // AI is thinking/reasoning\n | 'searching' // Web search\n | 'reading' // Reading files\n | 'writing' // Writing files \n | 'executing' // Running commands\n | 'analyzing' // Analyzing data\n | 'idle'; // No active operation\n\nexport interface ProgressUpdate {\n stage: ProgressStage;\n detail?: string;\n toolName?: string;\n toolArgs?: Record<string, unknown>;\n timestamp: number;\n}\n\nexport interface ProgressFeedbackConfig {\n // Feedback level: minimal (errors only), normal (key events), verbose (all updates)\n level: 'minimal' | 'normal' | 'verbose';\n // Whether to show thinking indicators\n showThinking: boolean;\n // Whether to stream tool progress updates\n streamToolProgress: boolean;\n // Whether to send periodic heartbeats for long tasks\n heartbeatEnabled: boolean;\n // Heartbeat interval in ms\n heartbeatIntervalMs: number;\n // Threshold for long-running task in ms\n longTaskThresholdMs: number;\n // Enhanced feedback options\n showErrorAccumulation: boolean; // Show tool error accumulation (default: true)\n showRequestLimitWarning: boolean; // Show request limit warnings (default: true)\n}\n\nconst DEFAULT_CONFIG: ProgressFeedbackConfig = {\n level: 'normal',\n showThinking: true,\n streamToolProgress: true,\n heartbeatEnabled: true,\n heartbeatIntervalMs: 20000, // 20 seconds\n longTaskThresholdMs: 30000, // 30 seconds\n showErrorAccumulation: true,\n showRequestLimitWarning: true,\n};\n\n// Tool category mapping for better feedback\nconst TOOL_STAGE_MAP: Record<string, ProgressStage> = {\n // Reading tools\n 'read': 'reading',\n 'read_file': 'reading',\n 'read_multiple': 'reading',\n 'glob': 'reading',\n 'find': 'reading',\n 'list_dir': 'reading',\n 'ls': 'reading',\n 'memory_get': 'reading',\n 'curated_memory': 'writing',\n\n // Searching tools\n 'grep': 'searching',\n 'web_search': 'searching',\n 'search': 'searching',\n 'memory_search': 'searching',\n 'session_search': 'searching',\n 'find_files': 'searching',\n \n // Web fetching\n 'web_fetch': 'reading',\n 'fetch': 'reading',\n 'curl': 'reading',\n 'wget': 'reading',\n \n // Writing tools\n 'write': 'writing',\n 'write_file': 'writing',\n 'write_multiple': 'writing',\n 'edit': 'writing',\n 'apply_patch': 'writing',\n 'patch': 'writing',\n 'create': 'writing',\n 'mkdir': 'writing',\n 'delete': 'writing',\n 'remove': 'writing',\n \n // Executing tools\n 'bash': 'executing',\n 'exec_command': 'executing',\n 'update_plan': 'thinking',\n 'exec': 'executing',\n 'run': 'executing',\n 'command': 'executing',\n 'node': 'executing',\n 'python': 'executing',\n 'npm': 'executing',\n 'pnpm': 'executing',\n 'git': 'executing',\n 'docker': 'executing',\n 'kubectl': 'executing',\n \n // Message/communication\n 'message': 'idle',\n 'send': 'idle',\n 'reply': 'idle',\n \n // Analysis tools\n 'analyze': 'analyzing',\n 'compare': 'analyzing',\n 'diff': 'analyzing',\n 'summarize': 'analyzing',\n};\n\n// Emoji mapping for stages\nconst STAGE_EMOJI: Record<ProgressStage, string> = {\n 'thinking': '🤔',\n 'searching': '🔍',\n 'reading': '📖',\n 'writing': '✍️',\n 'executing': '⚙️',\n 'analyzing': '📊',\n 'idle': '💬',\n};\n\n// Stage labels in English\nconst STAGE_LABEL: Record<ProgressStage, string> = {\n 'thinking': 'Thinking',\n 'searching': 'Searching',\n 'reading': 'Reading',\n 'writing': 'Writing',\n 'executing': 'Executing',\n 'analyzing': 'Analyzing',\n 'idle': 'Ready',\n};\n\nexport interface ProgressMessage {\n type: 'start' | 'update' | 'complete' | 'error' | 'warning' | 'thinking';\n stage: ProgressStage;\n message: string;\n detail?: string;\n toolName?: string;\n toolArgs?: Record<string, unknown>;\n}\n\nexport interface ProgressCallbacks {\n onProgress?: (msg: ProgressMessage) => void;\n onStreamStart?: (toolName: string, toolArgs: Record<string, unknown>) => void;\n onStreamUpdate?: (toolName: string, partialResult: string) => void;\n onStreamEnd?: (toolName: string, result: string, isError: boolean) => void;\n onThinking?: (thinking: string) => void;\n onHeartbeat?: (elapsedMs: number, currentStage: ProgressStage) => void;\n}\n\nexport class ProgressFeedbackManager {\n private config: ProgressFeedbackConfig;\n private callbacks: ProgressCallbacks = {};\n private currentStage: ProgressStage = 'idle';\n private currentTool: string | null = null;\n private taskStartTime: number = 0;\n private heartbeatTimer?: ReturnType<typeof setInterval>;\n private streamActive: boolean = false;\n\n constructor(config: Partial<ProgressFeedbackConfig> = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n setCallbacks(callbacks: ProgressCallbacks): void {\n this.callbacks = { ...this.callbacks, ...callbacks };\n }\n\n startTask(): void {\n this.taskStartTime = Date.now();\n \n if (this.config.heartbeatEnabled) {\n this.startHeartbeat();\n }\n }\n\n endTask(): void {\n this.stopHeartbeat();\n this.currentStage = 'idle';\n this.currentTool = null;\n this.taskStartTime = 0;\n this.streamActive = false;\n }\n\n private startHeartbeat(): void {\n this.stopHeartbeat();\n this.heartbeatTimer = setInterval(() => {\n const elapsed = Date.now() - this.taskStartTime;\n if (elapsed >= this.config.longTaskThresholdMs) {\n this.callbacks.onHeartbeat?.(elapsed, this.currentStage);\n }\n }, this.config.heartbeatIntervalMs);\n }\n\n private stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = undefined;\n }\n }\n\n private mapToolToStage(toolName: string): ProgressStage {\n const normalizedName = toolName.toLowerCase();\n const normalizedNoUnderscore = normalizedName.replace(/_/g, '');\n \n // Try both versions: with and without underscores\n const toCheck = [normalizedName, normalizedNoUnderscore];\n \n // Sort keys by length (longest first) to match more specific keys first\n const sortedKeys = Object.keys(TOOL_STAGE_MAP).sort((a, b) => b.length - a.length);\n \n for (const name of toCheck) {\n for (const key of sortedKeys) {\n if (name.includes(key)) {\n return TOOL_STAGE_MAP[key];\n }\n }\n }\n return 'executing';\n }\n\n private formatToolName(toolName: string): string {\n // Convert snake_case to Title Case\n return toolName\n .replace(/_/g, ' ')\n .replace(/\\b\\w/g, c => c.toUpperCase());\n }\n\n // Handle tool execution start\n onToolStart(toolName: string, toolArgs: Record<string, unknown> = {}): void {\n if (this.config.level === 'minimal') return;\n\n this.currentTool = toolName;\n this.currentStage = this.mapToolToStage(toolName);\n this.streamActive = true;\n\n const formattedName = this.formatToolName(toolName);\n const emoji = STAGE_EMOJI[this.currentStage];\n const label = STAGE_LABEL[this.currentStage];\n\n // Build detail message\n let detail = '';\n if (toolArgs.path) {\n detail = `📁 ${toolArgs.path}`;\n } else if (toolArgs.query) {\n detail = `🔎 ${toolArgs.query}`;\n } else if (toolArgs.command) {\n const cmd = String(toolArgs.command);\n detail = `⚡ ${cmd.length > 50 ? cmd.slice(0, 50) + '...' : cmd}`;\n }\n\n const message: ProgressMessage = {\n type: 'start',\n stage: this.currentStage,\n message: `${emoji} ${label}: ${formattedName}`,\n detail: detail || undefined,\n toolName,\n toolArgs,\n };\n\n if (this.config.level === 'verbose' || this.config.streamToolProgress) {\n this.callbacks.onProgress?.(message);\n }\n this.callbacks.onStreamStart?.(toolName, toolArgs);\n\n log.debug({ toolName, stage: this.currentStage }, 'Tool execution started');\n }\n\n // Handle tool execution update (streaming)\n onToolUpdate(toolName: string, partialResult: unknown): void {\n if (this.config.level === 'minimal') return;\n if (!this.streamActive || !this.config.streamToolProgress) return;\n\n // Format partial result for display\n let displayResult = '';\n if (typeof partialResult === 'string') {\n displayResult = partialResult.slice(0, 200); // Truncate for display\n } else if (partialResult && typeof partialResult === 'object') {\n const result = partialResult as Record<string, unknown>;\n if (result.output) {\n displayResult = String(result.output).slice(0, 200);\n } else if (result.content) {\n displayResult = String(result.content).slice(0, 200);\n } else if (result.text) {\n displayResult = String(result.text).slice(0, 200);\n }\n }\n\n this.callbacks.onStreamUpdate?.(toolName, displayResult);\n }\n\n // Handle tool execution end\n onToolEnd(toolName: string, result: unknown, isError: boolean = false): void {\n if (this.config.level === 'minimal' && !isError) return;\n\n const wasActive = this.streamActive;\n this.streamActive = false;\n\n // Extract result summary\n let resultSummary = '';\n if (result && typeof result === 'object') {\n const res = result as Record<string, unknown>;\n if (res.content && Array.isArray(res.content)) {\n const textContent = res.content.find((c: unknown) => \n typeof c === 'object' && c !== null && (c as Record<string, unknown>).type === 'text'\n );\n if (textContent) {\n resultSummary = String((textContent as Record<string, unknown>).text || '').slice(0, 100);\n }\n }\n }\n\n const message: ProgressMessage = {\n type: isError ? 'error' : 'complete',\n stage: this.currentStage,\n message: isError \n ? `❌ Tool failed: ${this.formatToolName(toolName)}`\n : `✅ Done: ${this.formatToolName(toolName)}`,\n toolName,\n };\n\n if (wasActive) {\n this.callbacks.onStreamEnd?.(toolName, resultSummary, isError);\n }\n \n if (isError || this.config.level !== 'minimal') {\n this.callbacks.onProgress?.(message);\n }\n\n // Reset current tool if it matches\n if (this.currentTool === toolName) {\n this.currentTool = null;\n this.currentStage = 'idle';\n }\n\n log.debug({ toolName, isError }, 'Tool execution ended');\n }\n\n // Handle thinking/reasoning\n onThinking(thinking: string): void {\n if (!this.config.showThinking || this.config.level === 'minimal') return;\n\n const displayThinking = thinking.length > 100 \n ? thinking.slice(0, 100) + '...'\n : thinking;\n\n this.callbacks.onThinking?.(displayThinking);\n }\n\n // Handle turn start\n onTurnStart(): void {\n if (this.config.level === 'minimal') return;\n \n this.currentStage = 'thinking';\n this.taskStartTime = Date.now();\n \n if (this.config.heartbeatEnabled && !this.heartbeatTimer) {\n this.startHeartbeat();\n }\n }\n\n // Handle message/response start\n onMessageStart(): void {\n // Could be used to show \"AI is typing...\" indicator\n }\n\n // Handle message/response update (streaming tokens)\n onMessageUpdate(_content: string): void {\n // Could be used to show live response\n }\n\n // Handle agent end\n onAgentEnd(): void {\n this.endTask();\n }\n\n // Get current state\n getCurrentState(): { stage: ProgressStage; tool: string | null; elapsedMs: number } {\n return {\n stage: this.currentStage,\n tool: this.currentTool,\n elapsedMs: this.taskStartTime ? Date.now() - this.taskStartTime : 0,\n };\n }\n\n // Update config\n setConfig(config: Partial<ProgressFeedbackConfig>): void {\n this.config = { ...this.config, ...config };\n }\n\n // Get stage emoji\n getStageEmoji(stage: ProgressStage): string {\n return STAGE_EMOJI[stage] || '💬';\n }\n\n // Get stage label\n getStageLabel(stage: ProgressStage): string {\n return STAGE_LABEL[stage] || 'Unknown';\n }\n\n /**\n * Notify about accumulated tool errors\n * Enhanced feedback for tool failures\n */\n onToolErrorAccumulated(\n toolName: string,\n failureCount: number,\n maxFailures: number,\n remainingAttempts?: number\n ): void {\n if (!this.config.showErrorAccumulation) return;\n if (this.config.level === 'minimal') return;\n\n const remaining = remainingAttempts ?? (maxFailures - failureCount);\n const percentage = (failureCount / maxFailures) * 100;\n \n let message: string;\n let type: 'warning' | 'error' = percentage >= 100 ? 'error' : 'warning';\n\n if (percentage >= 100) {\n message = `❌ ${this.formatToolName(toolName)} has reached maximum failures (${maxFailures}/${maxFailures})`;\n } else if (percentage >= 50) {\n message = `⚠️ ${this.formatToolName(toolName)} failed ${failureCount}/${maxFailures} times`;\n } else {\n message = `⚠️ ${this.formatToolName(toolName)} failed ${failureCount} time${failureCount > 1 ? 's' : ''}`;\n }\n\n const progressMsg: ProgressMessage = {\n type,\n stage: 'executing',\n message,\n detail: remaining > 0 \n ? `Remaining attempts: ${remaining}` \n : 'Stopping tool execution',\n toolName,\n };\n\n this.callbacks.onProgress?.(progressMsg);\n\n const atCap = maxFailures > 0 && failureCount >= maxFailures;\n const summary = atCap\n ? `Tool \"${toolName}\" reached max failures (${failureCount}/${maxFailures})`\n : `Tool \"${toolName}\" failures ${failureCount}/${maxFailures} (${remaining} attempt(s) left)`;\n if (atCap) {\n log.error({ toolName, failureCount, maxFailures, remaining }, summary);\n } else {\n log.warn({ toolName, failureCount, maxFailures, remaining }, summary);\n }\n }\n\n /**\n * Notify about request limit status\n * Enhanced feedback for approaching/ reaching request limits\n */\n onRequestLimitStatus(\n count: number,\n limit: number,\n remaining: number,\n isWarning: boolean,\n shouldStop: boolean\n ): void {\n if (!this.config.showRequestLimitWarning) return;\n if (this.config.level === 'minimal') return;\n if (!isWarning && !shouldStop) return;\n\n const percentage = (count / limit) * 100;\n \n let message: string;\n let type: 'warning' | 'error' = shouldStop ? 'error' : 'warning';\n\n if (shouldStop) {\n message = `❌ Request limit reached (${count}/${limit})`;\n } else if (percentage >= 90) {\n message = `⚠️ Request limit warning (${count}/${limit})`;\n } else {\n message = `⚠️ Approaching request limit (${count}/${limit})`;\n }\n\n const progressMsg: ProgressMessage = {\n type,\n stage: 'thinking',\n message,\n detail: remaining > 0 \n ? `Remaining requests: ${remaining}` \n : 'Stopping execution',\n };\n\n this.callbacks.onProgress?.(progressMsg);\n\n const summary = shouldStop\n ? `LLM request limit reached (${count}/${limit}); stopping turn`\n : `LLM request budget ${count}/${limit} (${remaining} left)`;\n if (shouldStop) {\n log.error({ count, limit, remaining, shouldStop }, summary);\n } else {\n log.warn({ count, limit, remaining, shouldStop }, summary);\n }\n }\n}\n\n// Singleton instance with default config\nexport const progressFeedbackManager = new ProgressFeedbackManager();\n\n// Utility function to format progress messages for Telegram\nexport function formatProgressMessage(msg: ProgressMessage, parseMode: 'Markdown' | 'HTML' = 'HTML'): string {\n const emoji = STAGE_EMOJI[msg.stage];\n \n if (parseMode === 'HTML') {\n let html = `<b>${emoji} ${msg.message}</b>`;\n if (msg.detail) {\n html += `\\n${msg.detail}`;\n }\n return html;\n } else {\n let md = `**${emoji} ${msg.message}**`;\n if (msg.detail) {\n md += `\\n${msg.detail}`;\n }\n return md;\n }\n}\n\n// Utility function to format heartbeat message\nexport function formatHeartbeatMessage(elapsedMs: number, stage: ProgressStage, detail?: string): string {\n const seconds = Math.floor(elapsedMs / 1000);\n const emoji = STAGE_EMOJI[stage];\n const label = STAGE_LABEL[stage];\n \n let message = `${emoji} Still ${label.toLowerCase()}...`;\n if (detail) {\n message += `\\n${detail}`;\n }\n message += `\\n⏱️ ${seconds}s elapsed`;\n \n return message;\n}\n"],"mappings":"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"}
@@ -33,8 +33,8 @@ export interface ToolTimeoutConfig {
33
33
  * @example
34
34
  * ```typescript
35
35
  * const result = await executeWithTimeout(
36
- * () => shell.exec('long-running-command'),
37
- * { toolName: 'shell', timeoutMs: 60000 }
36
+ * () => execCommand('long-running-command'),
37
+ * { toolName: 'exec_command', timeoutMs: 60000 }
38
38
  * );
39
39
  * ```
40
40
  */
@@ -16,7 +16,7 @@ const DEFAULT_CONFIG = {
16
16
  */
17
17
  function getTimeoutForTool(toolName, config) {
18
18
  const toolLower = toolName.toLowerCase();
19
- if (toolLower.includes("shell") || toolLower.includes("exec")) return config.shellTimeoutMs;
19
+ if (toolLower.includes("exec") || toolLower.includes("command")) return config.shellTimeoutMs;
20
20
  if (toolLower.includes("read") || toolLower.includes("view")) return config.readTimeoutMs;
21
21
  if (toolLower.includes("write") || toolLower.includes("edit")) return config.writeTimeoutMs;
22
22
  if (toolLower.includes("web") || toolLower.includes("http") || toolLower.includes("fetch")) return config.networkTimeoutMs;
@@ -28,8 +28,8 @@ function getTimeoutForTool(toolName, config) {
28
28
  * @example
29
29
  * ```typescript
30
30
  * const result = await executeWithTimeout(
31
- * () => shell.exec('long-running-command'),
32
- * { toolName: 'shell', timeoutMs: 60000 }
31
+ * () => execCommand('long-running-command'),
32
+ * { toolName: 'exec_command', timeoutMs: 60000 }
33
33
  * );
34
34
  * ```
35
35
  */
@@ -123,7 +123,7 @@ var TimeoutError = class TimeoutError extends Error {
123
123
  */
124
124
  getUserMessage() {
125
125
  const suggestions = [];
126
- if (this.toolName.toLowerCase().includes("shell")) {
126
+ if (this.toolName.toLowerCase().includes("exec") || this.toolName.toLowerCase().includes("command")) {
127
127
  suggestions.push("Break the command into smaller steps");
128
128
  suggestions.push("Add timeouts to individual commands");
129
129
  suggestions.push("Check for infinite loops or waiting for input");
@@ -1 +1 @@
1
- {"version":3,"file":"timeout-wrapper.js","names":[],"sources":["../../../../src/agent/lifecycle/timeout-wrapper.ts"],"sourcesContent":["/**\n * Timeout Wrapper - Protects tool execution from hanging\n *\n * Wraps tool execution with timeout protection to prevent:\n * - Infinite loops in shell commands\n * - Network requests hanging forever\n * - Resource exhaustion\n */\n\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('TimeoutWrapper');\n\nexport interface TimeoutConfig {\n defaultTimeoutMs: number; // Default timeout in ms (default: 300000 = 5 min)\n shellTimeoutMs: number; // Shell command timeout (default: 300000 = 5 min)\n readTimeoutMs: number; // File read timeout (default: 30000 = 30 sec)\n writeTimeoutMs: number; // File write timeout (default: 60000 = 1 min)\n networkTimeoutMs: number; // Network request timeout (default: 60000 = 1 min)\n gracefulShutdownMs: number; // Time to wait for graceful shutdown (default: 5000)\n}\n\nconst DEFAULT_CONFIG: TimeoutConfig = {\n defaultTimeoutMs: 5 * 60 * 1000, // 5 minutes\n shellTimeoutMs: 5 * 60 * 1000, // 5 minutes\n readTimeoutMs: 30 * 1000, // 30 seconds\n writeTimeoutMs: 60 * 1000, // 1 minute\n networkTimeoutMs: 60 * 1000, // 1 minute\n gracefulShutdownMs: 5000, // 5 seconds\n};\n\nexport interface TimeoutResult<T> {\n success: boolean;\n result?: T;\n error?: Error;\n timedOut: boolean;\n executionTimeMs: number;\n}\n\nexport interface ToolTimeoutConfig {\n toolName: string;\n timeoutMs?: number;\n description?: string;\n}\n\n/**\n * Get timeout for specific tool type\n */\nfunction getTimeoutForTool(toolName: string, config: TimeoutConfig): number {\n const toolLower = toolName.toLowerCase();\n\n if (toolLower.includes('shell') || toolLower.includes('exec')) {\n return config.shellTimeoutMs;\n }\n if (toolLower.includes('read') || toolLower.includes('view')) {\n return config.readTimeoutMs;\n }\n if (toolLower.includes('write') || toolLower.includes('edit')) {\n return config.writeTimeoutMs;\n }\n if (toolLower.includes('web') || toolLower.includes('http') || toolLower.includes('fetch')) {\n return config.networkTimeoutMs;\n }\n\n return config.defaultTimeoutMs;\n}\n\n/**\n * Execute function with timeout protection\n *\n * @example\n * ```typescript\n * const result = await executeWithTimeout(\n * () => shell.exec('long-running-command'),\n * { toolName: 'shell', timeoutMs: 60000 }\n * );\n * ```\n */\nexport async function executeWithTimeout<T>(\n operation: () => Promise<T>,\n config: ToolTimeoutConfig\n): Promise<T> {\n const fullConfig = { ...DEFAULT_CONFIG };\n const timeoutMs = config.timeoutMs || getTimeoutForTool(config.toolName, fullConfig);\n\n const startTime = Date.now();\n let timeoutId: NodeJS.Timeout | null = null;\n let isSettled = false;\n\n return new Promise((resolve, reject) => {\n // Set up timeout\n timeoutId = setTimeout(() => {\n if (isSettled) return;\n isSettled = true;\n \n const executionTime = Date.now() - startTime;\n const desc = config.description ? ` (${config.description})` : '';\n log.error(\n {\n toolName: config.toolName,\n timeoutMs,\n executionTimeMs: executionTime,\n description: config.description,\n },\n `Tool \"${config.toolName}\" timed out after ${timeoutMs}ms (elapsed ${executionTime}ms)${desc}`,\n );\n\n reject(\n new TimeoutError(\n config.toolName,\n timeoutMs,\n config.description,\n executionTime\n )\n );\n }, timeoutMs);\n\n // Execute operation\n operation()\n .then(result => {\n if (isSettled) return;\n isSettled = true;\n \n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n const executionTime = Date.now() - startTime;\n\n log.debug(\n {\n toolName: config.toolName,\n executionTimeMs: executionTime,\n },\n 'Tool execution completed within timeout'\n );\n\n resolve(result);\n })\n .catch(error => {\n if (isSettled) return;\n isSettled = true;\n \n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n reject(error);\n });\n });\n}\n\n/**\n * Execute with timeout and return detailed result\n */\nexport async function executeWithTimeoutResult<T>(\n operation: () => Promise<T>,\n config: ToolTimeoutConfig\n): Promise<TimeoutResult<T>> {\n const startTime = Date.now();\n\n try {\n const result = await executeWithTimeout(operation, config);\n return {\n success: true,\n result,\n timedOut: false,\n executionTimeMs: Date.now() - startTime,\n };\n } catch (error) {\n const isTimeout = error instanceof TimeoutError;\n return {\n success: false,\n error: error instanceof Error ? error : new Error(String(error)),\n timedOut: isTimeout,\n executionTimeMs: Date.now() - startTime,\n };\n }\n}\n\n/**\n * Custom timeout error\n */\nexport class TimeoutError extends Error {\n public readonly toolName: string;\n public readonly timeoutMs: number;\n public readonly executionTimeMs: number;\n public readonly description?: string;\n\n constructor(\n toolName: string,\n timeoutMs: number,\n description?: string,\n executionTimeMs: number = timeoutMs\n ) {\n const timeoutSec = Math.round(timeoutMs / 1000);\n const message = description\n ? `Tool '${toolName}' timed out after ${timeoutSec}s: ${description}`\n : `Tool '${toolName}' timed out after ${timeoutSec}s. Consider breaking the operation into smaller steps.`;\n\n super(message);\n this.name = 'TimeoutError';\n this.toolName = toolName;\n this.timeoutMs = timeoutMs;\n this.description = description;\n this.executionTimeMs = executionTimeMs;\n\n // Maintain proper stack trace\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, TimeoutError);\n }\n }\n\n /**\n * Get user-friendly error message with suggestions\n */\n getUserMessage(): string {\n const suggestions: string[] = [];\n\n if (this.toolName.toLowerCase().includes('shell')) {\n suggestions.push('Break the command into smaller steps');\n suggestions.push('Add timeouts to individual commands');\n suggestions.push('Check for infinite loops or waiting for input');\n } else if (this.toolName.toLowerCase().includes('read')) {\n suggestions.push('Read the file in smaller chunks');\n suggestions.push('Use grep to find specific content first');\n } else if (this.toolName.toLowerCase().includes('write')) {\n suggestions.push('Write content in smaller batches');\n } else if (this.toolName.toLowerCase().includes('web')) {\n suggestions.push('Check network connectivity');\n suggestions.push('Try again later');\n }\n\n let message = `⚠️ ${this.message}`;\n if (suggestions.length > 0) {\n message += '\\n\\nSuggestions:\\n' + suggestions.map(s => `• ${s}`).join('\\n');\n }\n\n return message;\n }\n}\n\n/**\n * Wrap a function with timeout protection\n */\nexport function withTimeout<T extends (...args: any[]) => Promise<any>>(\n fn: T,\n config: Omit<ToolTimeoutConfig, 'description'> & { description?: string | ((...args: Parameters<T>) => string) }\n): (...args: Parameters<T>) => Promise<ReturnType<T>> {\n return async (...args: Parameters<T>): Promise<ReturnType<T>> => {\n const description = typeof config.description === 'function'\n ? config.description(...args)\n : config.description;\n\n return executeWithTimeout(() => fn(...args), {\n toolName: config.toolName,\n timeoutMs: config.timeoutMs,\n description,\n });\n };\n}\n\n/**\n * Timeout manager for tracking execution statistics\n */\nexport class TimeoutManager {\n private executions: Array<{\n toolName: string;\n executionTimeMs: number;\n timedOut: boolean;\n timestamp: number;\n }> = [];\n\n constructor(_config: Partial<TimeoutConfig> = {}) {}\n\n /**\n * Execute with timeout and track statistics\n */\n async execute<T>(\n operation: () => Promise<T>,\n toolConfig: ToolTimeoutConfig\n ): Promise<T> {\n const startTime = Date.now();\n let timedOut = false;\n\n try {\n const result = await executeWithTimeout(operation, toolConfig);\n this.executions.push({\n toolName: toolConfig.toolName,\n executionTimeMs: Date.now() - startTime,\n timedOut: false,\n timestamp: Date.now(),\n });\n return result;\n } catch (error) {\n if (error instanceof TimeoutError) {\n timedOut = true;\n }\n this.executions.push({\n toolName: toolConfig.toolName,\n executionTimeMs: Date.now() - startTime,\n timedOut,\n timestamp: Date.now(),\n });\n throw error;\n }\n }\n\n /**\n * Get execution statistics\n */\n getStats(): {\n totalExecutions: number;\n timeoutCount: number;\n timeoutRate: number;\n averageExecutionTimeMs: number;\n byTool: Record<string, {\n count: number;\n timeouts: number;\n avgExecutionTimeMs: number;\n }>;\n } {\n const byTool: Record<string, { count: number; timeouts: number; totalTime: number }> = {};\n\n for (const exec of this.executions) {\n if (!byTool[exec.toolName]) {\n byTool[exec.toolName] = { count: 0, timeouts: 0, totalTime: 0 };\n }\n byTool[exec.toolName].count++;\n if (exec.timedOut) {\n byTool[exec.toolName].timeouts++;\n }\n byTool[exec.toolName].totalTime += exec.executionTimeMs;\n }\n\n const totalExecutions = this.executions.length;\n const timeoutCount = this.executions.filter(e => e.timedOut).length;\n const totalTime = this.executions.reduce((sum, e) => sum + e.executionTimeMs, 0);\n\n return {\n totalExecutions,\n timeoutCount,\n timeoutRate: totalExecutions > 0 ? timeoutCount / totalExecutions : 0,\n averageExecutionTimeMs: totalExecutions > 0 ? totalTime / totalExecutions : 0,\n byTool: Object.fromEntries(\n Object.entries(byTool).map(([name, stats]) => [\n name,\n {\n count: stats.count,\n timeouts: stats.timeouts,\n avgExecutionTimeMs: stats.count > 0 ? stats.totalTime / stats.count : 0,\n },\n ])\n ),\n };\n }\n\n /**\n * Clean up old execution records\n */\n cleanup(maxAgeMs: number = 24 * 60 * 60 * 1000): void {\n const cutoff = Date.now() - maxAgeMs;\n this.executions = this.executions.filter(e => e.timestamp > cutoff);\n }\n}\n\n// Convenience exports\nexport { DEFAULT_CONFIG as DEFAULT_TIMEOUT_CONFIG };\n"],"mappings":"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"}
1
+ {"version":3,"file":"timeout-wrapper.js","names":[],"sources":["../../../../src/agent/lifecycle/timeout-wrapper.ts"],"sourcesContent":["/**\n * Timeout Wrapper - Protects tool execution from hanging\n *\n * Wraps tool execution with timeout protection to prevent:\n * - Infinite loops in shell commands\n * - Network requests hanging forever\n * - Resource exhaustion\n */\n\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('TimeoutWrapper');\n\nexport interface TimeoutConfig {\n defaultTimeoutMs: number; // Default timeout in ms (default: 300000 = 5 min)\n shellTimeoutMs: number; // Shell command timeout (default: 300000 = 5 min)\n readTimeoutMs: number; // File read timeout (default: 30000 = 30 sec)\n writeTimeoutMs: number; // File write timeout (default: 60000 = 1 min)\n networkTimeoutMs: number; // Network request timeout (default: 60000 = 1 min)\n gracefulShutdownMs: number; // Time to wait for graceful shutdown (default: 5000)\n}\n\nconst DEFAULT_CONFIG: TimeoutConfig = {\n defaultTimeoutMs: 5 * 60 * 1000, // 5 minutes\n shellTimeoutMs: 5 * 60 * 1000, // 5 minutes\n readTimeoutMs: 30 * 1000, // 30 seconds\n writeTimeoutMs: 60 * 1000, // 1 minute\n networkTimeoutMs: 60 * 1000, // 1 minute\n gracefulShutdownMs: 5000, // 5 seconds\n};\n\nexport interface TimeoutResult<T> {\n success: boolean;\n result?: T;\n error?: Error;\n timedOut: boolean;\n executionTimeMs: number;\n}\n\nexport interface ToolTimeoutConfig {\n toolName: string;\n timeoutMs?: number;\n description?: string;\n}\n\n/**\n * Get timeout for specific tool type\n */\nfunction getTimeoutForTool(toolName: string, config: TimeoutConfig): number {\n const toolLower = toolName.toLowerCase();\n\n if (toolLower.includes('exec') || toolLower.includes('command')) {\n return config.shellTimeoutMs;\n }\n if (toolLower.includes('read') || toolLower.includes('view')) {\n return config.readTimeoutMs;\n }\n if (toolLower.includes('write') || toolLower.includes('edit')) {\n return config.writeTimeoutMs;\n }\n if (toolLower.includes('web') || toolLower.includes('http') || toolLower.includes('fetch')) {\n return config.networkTimeoutMs;\n }\n\n return config.defaultTimeoutMs;\n}\n\n/**\n * Execute function with timeout protection\n *\n * @example\n * ```typescript\n * const result = await executeWithTimeout(\n * () => execCommand('long-running-command'),\n * { toolName: 'exec_command', timeoutMs: 60000 }\n * );\n * ```\n */\nexport async function executeWithTimeout<T>(\n operation: () => Promise<T>,\n config: ToolTimeoutConfig\n): Promise<T> {\n const fullConfig = { ...DEFAULT_CONFIG };\n const timeoutMs = config.timeoutMs || getTimeoutForTool(config.toolName, fullConfig);\n\n const startTime = Date.now();\n let timeoutId: NodeJS.Timeout | null = null;\n let isSettled = false;\n\n return new Promise((resolve, reject) => {\n // Set up timeout\n timeoutId = setTimeout(() => {\n if (isSettled) return;\n isSettled = true;\n \n const executionTime = Date.now() - startTime;\n const desc = config.description ? ` (${config.description})` : '';\n log.error(\n {\n toolName: config.toolName,\n timeoutMs,\n executionTimeMs: executionTime,\n description: config.description,\n },\n `Tool \"${config.toolName}\" timed out after ${timeoutMs}ms (elapsed ${executionTime}ms)${desc}`,\n );\n\n reject(\n new TimeoutError(\n config.toolName,\n timeoutMs,\n config.description,\n executionTime\n )\n );\n }, timeoutMs);\n\n // Execute operation\n operation()\n .then(result => {\n if (isSettled) return;\n isSettled = true;\n \n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n const executionTime = Date.now() - startTime;\n\n log.debug(\n {\n toolName: config.toolName,\n executionTimeMs: executionTime,\n },\n 'Tool execution completed within timeout'\n );\n\n resolve(result);\n })\n .catch(error => {\n if (isSettled) return;\n isSettled = true;\n \n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n reject(error);\n });\n });\n}\n\n/**\n * Execute with timeout and return detailed result\n */\nexport async function executeWithTimeoutResult<T>(\n operation: () => Promise<T>,\n config: ToolTimeoutConfig\n): Promise<TimeoutResult<T>> {\n const startTime = Date.now();\n\n try {\n const result = await executeWithTimeout(operation, config);\n return {\n success: true,\n result,\n timedOut: false,\n executionTimeMs: Date.now() - startTime,\n };\n } catch (error) {\n const isTimeout = error instanceof TimeoutError;\n return {\n success: false,\n error: error instanceof Error ? error : new Error(String(error)),\n timedOut: isTimeout,\n executionTimeMs: Date.now() - startTime,\n };\n }\n}\n\n/**\n * Custom timeout error\n */\nexport class TimeoutError extends Error {\n public readonly toolName: string;\n public readonly timeoutMs: number;\n public readonly executionTimeMs: number;\n public readonly description?: string;\n\n constructor(\n toolName: string,\n timeoutMs: number,\n description?: string,\n executionTimeMs: number = timeoutMs\n ) {\n const timeoutSec = Math.round(timeoutMs / 1000);\n const message = description\n ? `Tool '${toolName}' timed out after ${timeoutSec}s: ${description}`\n : `Tool '${toolName}' timed out after ${timeoutSec}s. Consider breaking the operation into smaller steps.`;\n\n super(message);\n this.name = 'TimeoutError';\n this.toolName = toolName;\n this.timeoutMs = timeoutMs;\n this.description = description;\n this.executionTimeMs = executionTimeMs;\n\n // Maintain proper stack trace\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, TimeoutError);\n }\n }\n\n /**\n * Get user-friendly error message with suggestions\n */\n getUserMessage(): string {\n const suggestions: string[] = [];\n\n if (this.toolName.toLowerCase().includes('exec') || this.toolName.toLowerCase().includes('command')) {\n suggestions.push('Break the command into smaller steps');\n suggestions.push('Add timeouts to individual commands');\n suggestions.push('Check for infinite loops or waiting for input');\n } else if (this.toolName.toLowerCase().includes('read')) {\n suggestions.push('Read the file in smaller chunks');\n suggestions.push('Use grep to find specific content first');\n } else if (this.toolName.toLowerCase().includes('write')) {\n suggestions.push('Write content in smaller batches');\n } else if (this.toolName.toLowerCase().includes('web')) {\n suggestions.push('Check network connectivity');\n suggestions.push('Try again later');\n }\n\n let message = `⚠️ ${this.message}`;\n if (suggestions.length > 0) {\n message += '\\n\\nSuggestions:\\n' + suggestions.map(s => `• ${s}`).join('\\n');\n }\n\n return message;\n }\n}\n\n/**\n * Wrap a function with timeout protection\n */\nexport function withTimeout<T extends (...args: any[]) => Promise<any>>(\n fn: T,\n config: Omit<ToolTimeoutConfig, 'description'> & { description?: string | ((...args: Parameters<T>) => string) }\n): (...args: Parameters<T>) => Promise<ReturnType<T>> {\n return async (...args: Parameters<T>): Promise<ReturnType<T>> => {\n const description = typeof config.description === 'function'\n ? config.description(...args)\n : config.description;\n\n return executeWithTimeout(() => fn(...args), {\n toolName: config.toolName,\n timeoutMs: config.timeoutMs,\n description,\n });\n };\n}\n\n/**\n * Timeout manager for tracking execution statistics\n */\nexport class TimeoutManager {\n private executions: Array<{\n toolName: string;\n executionTimeMs: number;\n timedOut: boolean;\n timestamp: number;\n }> = [];\n\n constructor(_config: Partial<TimeoutConfig> = {}) {}\n\n /**\n * Execute with timeout and track statistics\n */\n async execute<T>(\n operation: () => Promise<T>,\n toolConfig: ToolTimeoutConfig\n ): Promise<T> {\n const startTime = Date.now();\n let timedOut = false;\n\n try {\n const result = await executeWithTimeout(operation, toolConfig);\n this.executions.push({\n toolName: toolConfig.toolName,\n executionTimeMs: Date.now() - startTime,\n timedOut: false,\n timestamp: Date.now(),\n });\n return result;\n } catch (error) {\n if (error instanceof TimeoutError) {\n timedOut = true;\n }\n this.executions.push({\n toolName: toolConfig.toolName,\n executionTimeMs: Date.now() - startTime,\n timedOut,\n timestamp: Date.now(),\n });\n throw error;\n }\n }\n\n /**\n * Get execution statistics\n */\n getStats(): {\n totalExecutions: number;\n timeoutCount: number;\n timeoutRate: number;\n averageExecutionTimeMs: number;\n byTool: Record<string, {\n count: number;\n timeouts: number;\n avgExecutionTimeMs: number;\n }>;\n } {\n const byTool: Record<string, { count: number; timeouts: number; totalTime: number }> = {};\n\n for (const exec of this.executions) {\n if (!byTool[exec.toolName]) {\n byTool[exec.toolName] = { count: 0, timeouts: 0, totalTime: 0 };\n }\n byTool[exec.toolName].count++;\n if (exec.timedOut) {\n byTool[exec.toolName].timeouts++;\n }\n byTool[exec.toolName].totalTime += exec.executionTimeMs;\n }\n\n const totalExecutions = this.executions.length;\n const timeoutCount = this.executions.filter(e => e.timedOut).length;\n const totalTime = this.executions.reduce((sum, e) => sum + e.executionTimeMs, 0);\n\n return {\n totalExecutions,\n timeoutCount,\n timeoutRate: totalExecutions > 0 ? timeoutCount / totalExecutions : 0,\n averageExecutionTimeMs: totalExecutions > 0 ? totalTime / totalExecutions : 0,\n byTool: Object.fromEntries(\n Object.entries(byTool).map(([name, stats]) => [\n name,\n {\n count: stats.count,\n timeouts: stats.timeouts,\n avgExecutionTimeMs: stats.count > 0 ? stats.totalTime / stats.count : 0,\n },\n ])\n ),\n };\n }\n\n /**\n * Clean up old execution records\n */\n cleanup(maxAgeMs: number = 24 * 60 * 60 * 1000): void {\n const cutoff = Date.now() - maxAgeMs;\n this.executions = this.executions.filter(e => e.timestamp > cutoff);\n }\n}\n\n// Convenience exports\nexport { DEFAULT_CONFIG as DEFAULT_TIMEOUT_CONFIG };\n"],"mappings":"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"}
@@ -12,7 +12,7 @@ import type { AgentMessage } from '@earendil-works/pi-agent-core';
12
12
  export interface ToolCallSummary {
13
13
  toolName: string;
14
14
  filePath?: string;
15
- operation: 'read' | 'write' | 'search' | 'shell' | 'other';
15
+ operation: 'read' | 'write' | 'search' | 'command' | 'other';
16
16
  success: boolean;
17
17
  briefResult?: string;
18
18
  timestamp?: number;
@@ -43,11 +43,10 @@ function categorizeToolOperation(toolName) {
43
43
  ].some((n) => name.includes(n))) return "search";
44
44
  if ([
45
45
  "bash",
46
- "shell",
47
46
  "exec",
48
47
  "run",
49
48
  "command"
50
- ].some((n) => name.includes(n))) return "shell";
49
+ ].some((n) => name.includes(n))) return "command";
51
50
  return "other";
52
51
  }
53
52
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"summary-generator.js","names":[],"sources":["../../../../src/agent/memory/summary-generator.ts"],"sourcesContent":["/**\n * Summary Generator - Generates structured summaries for context compaction\n * \n * Creates structured summaries that preserve:\n * - User requests and intents\n * - Tool call history (deduplicated by file path)\n * - Key decisions and outcomes\n * - Modified files list\n * - Usage statistics\n */\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport type { ToolResultMessage, Message } from '@earendil-works/pi-ai';\nimport { createLogger } from '../../utils/logger.js';\nimport { readAgentMessageContent } from './agent-message-access.js';\n\nconst log = createLogger('SummaryGenerator');\n\nexport interface ToolCallSummary {\n toolName: string;\n filePath?: string;\n operation: 'read' | 'write' | 'search' | 'shell' | 'other';\n success: boolean;\n briefResult?: string;\n timestamp?: number;\n}\n\nexport interface ConversationSummary {\n userRequests: string[]; // User's core requests\n toolCalls: ToolCallSummary[]; // Tool call history\n keyDecisions: string[]; // Key decisions made\n filesModified: string[]; // List of modified files\n totalToolCalls: number; // Total tool calls (before dedup)\n successfulToolCalls: number; // Successful tool calls\n failedToolCalls: number; // Failed tool calls\n}\n\nexport interface SummaryGenerationOptions {\n maxUserRequests: number; // Max user requests to keep (default: 10)\n maxToolCalls: number; // Max tool calls to keep after dedup (default: 50)\n deduplicateByFile: boolean; // Deduplicate tool calls by file path (default: true)\n includeUsage: boolean; // Include usage statistics (default: true)\n}\n\nconst DEFAULT_OPTIONS: SummaryGenerationOptions = {\n maxUserRequests: 10,\n maxToolCalls: 50,\n deduplicateByFile: true,\n includeUsage: true,\n};\n\n/**\n * Categorize tool operation based on tool name\n */\nfunction categorizeToolOperation(toolName: string): 'read' | 'write' | 'search' | 'shell' | 'other' {\n const name = toolName.toLowerCase();\n \n if (['read', 'read_file', 'read_multiple', 'glob', 'list_dir', 'ls', 'memory_get'].some(n => name.includes(n))) {\n return 'read';\n }\n \n if (['write', 'write_file', 'write_multiple', 'edit', 'patch', 'create', 'mkdir'].some(n => name.includes(n))) {\n return 'write';\n }\n \n if (['grep', 'search', 'find', 'find_files', 'memory_search', 'web_search'].some(n => name.includes(n))) {\n return 'search';\n }\n \n if (['bash', 'shell', 'exec', 'run', 'command'].some(n => name.includes(n))) {\n return 'shell';\n }\n \n return 'other';\n}\n\n/**\n * Extract text content from message\n */\nfunction extractTextContent(content: unknown): string {\n if (typeof content === 'string') {\n return content;\n }\n \n if (Array.isArray(content)) {\n return content\n .filter(c => c.type === 'text')\n .map(c => (c as { text?: string }).text || '')\n .join('\\n');\n }\n \n return '';\n}\n\n/**\n * Type guard: Check if message has tool calls (assistant message)\n */\nfunction hasToolCalls(msg: AgentMessage): msg is AgentMessage & { toolCalls?: unknown[] } {\n return msg.role === 'assistant' && 'toolCalls' in msg;\n}\n\n/**\n * Type guard: Check if message is a tool result\n */\nfunction isToolResult(msg: AgentMessage): msg is AgentMessage & { name?: string; role: 'toolResult' } {\n return msg.role === 'toolResult' && 'name' in msg;\n}\n\n/**\n * Deduplicate tool calls by file path (keep last operation per file)\n */\nfunction deduplicateByFilePath(toolCalls: ToolCallSummary[]): ToolCallSummary[] {\n const byFile = new Map<string, ToolCallSummary>();\n const otherCalls: ToolCallSummary[] = [];\n\n for (const call of toolCalls) {\n if (call.filePath) {\n // Keep the last operation for each file\n byFile.set(call.filePath, call);\n } else {\n // Keep all non-file operations\n otherCalls.push(call);\n }\n }\n\n return [...Array.from(byFile.values()), ...otherCalls];\n}\n\n/**\n * Generate structured summary from messages\n */\nexport function generateStructuredSummary(\n messages: AgentMessage[],\n options: Partial<SummaryGenerationOptions> = {}\n): ConversationSummary {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n \n const summary: ConversationSummary = {\n userRequests: [],\n toolCalls: [],\n keyDecisions: [],\n filesModified: [],\n totalToolCalls: 0,\n successfulToolCalls: 0,\n failedToolCalls: 0,\n };\n\n let lastAssistantMessage: AgentMessage | null = null;\n\n for (const msg of messages) {\n // Extract user requests\n if (msg.role === 'user') {\n const text = extractTextContent(readAgentMessageContent(msg));\n if (text.trim().length > 20) {\n summary.userRequests.push(text.slice(0, 500)); // Limit each request length\n }\n }\n\n // Extract tool calls from assistant messages\n if (hasToolCalls(msg)) {\n lastAssistantMessage = msg;\n \n const toolCalls = (msg as Message & { toolCalls?: Array<{ name: string; args?: Record<string, unknown> }> }).toolCalls;\n if (toolCalls && toolCalls.length > 0) {\n summary.totalToolCalls += toolCalls.length;\n\n for (const call of toolCalls) {\n const toolSummary: ToolCallSummary = {\n toolName: call.name,\n operation: categorizeToolOperation(call.name),\n success: true, // Will be updated from tool_result\n filePath: String(call.args?.path || call.args?.file_path || call.args?.filePath || ''),\n timestamp: msg.timestamp,\n };\n summary.toolCalls.push(toolSummary);\n\n if (['write', 'edit'].includes(toolSummary.operation) && toolSummary.filePath) {\n summary.filesModified.push(toolSummary.filePath);\n }\n }\n }\n }\n\n // Extract tool results to determine success/failure\n if (isToolResult(msg)) {\n const toolMsg = msg as ToolResultMessage;\n const toolName = toolMsg.toolName;\n \n // Find the corresponding tool call and update success status\n const toolCall = summary.toolCalls\n .slice()\n .reverse()\n .find((tc: ToolCallSummary) => tc.toolName === toolName && !tc.timestamp); // Match by name, prefer unmatched\n \n if (toolCall) {\n const resultText = extractTextContent(readAgentMessageContent(toolMsg));\n \n // Detect failure from result content\n const isError = resultText.toLowerCase().includes('error') || \n resultText.toLowerCase().includes('failed') ||\n resultText.startsWith('Error:');\n \n toolCall.success = !isError;\n toolCall.briefResult = resultText.slice(0, 200);\n\n if (isError) {\n summary.failedToolCalls++;\n } else {\n summary.successfulToolCalls++;\n }\n }\n }\n }\n\n // Deduplicate tool calls by file path if enabled\n if (opts.deduplicateByFile) {\n summary.toolCalls = deduplicateByFilePath(summary.toolCalls);\n }\n\n // Limit tool calls\n if (summary.toolCalls.length > opts.maxToolCalls) {\n summary.toolCalls = summary.toolCalls.slice(-opts.maxToolCalls);\n }\n\n // Limit user requests\n if (summary.userRequests.length > opts.maxUserRequests) {\n summary.userRequests = summary.userRequests.slice(-opts.maxUserRequests);\n }\n\n // Deduplicate files modified\n summary.filesModified = [...new Set(summary.filesModified)];\n\n // Extract key decisions from last assistant message\n if (lastAssistantMessage) {\n const decisions = extractKeyDecisions(lastAssistantMessage);\n summary.keyDecisions = decisions;\n }\n\n log.debug(\n {\n userRequests: summary.userRequests.length,\n toolCalls: summary.toolCalls.length,\n filesModified: summary.filesModified.length,\n totalToolCalls: summary.totalToolCalls,\n successfulToolCalls: summary.successfulToolCalls,\n failedToolCalls: summary.failedToolCalls,\n },\n 'Generated structured summary'\n );\n\n return summary;\n}\n\n/**\n * Extract key decisions from assistant message\n */\nfunction extractKeyDecisions(msg: AgentMessage): string[] {\n const content = extractTextContent(readAgentMessageContent(msg));\n const decisions: string[] = [];\n\n // Look for decision patterns\n const decisionPatterns = [\n /(?:I'll|I will|Let's|We should|Decision:|Decided to|Chose to)\\s+(.+?)(?:\\.|\\n)/gi,\n /(?:Based on|After analyzing|Given that)\\s+(.+?)(?:\\.|\\n)/gi,\n /(?:The best approach|The solution|The answer) is\\s+(.+?)(?:\\.|\\n)/gi,\n ];\n\n for (const pattern of decisionPatterns) {\n const matches = content.match(pattern);\n if (matches) {\n decisions.push(...matches.slice(0, 5).map(m => m.trim()));\n }\n }\n\n // Limit decisions\n return decisions.slice(0, 10);\n}\n\n/**\n * Format structured summary as text for injection into context\n */\nexport function formatSummaryAsText(summary: ConversationSummary, includeUsage: boolean = true): string {\n const lines: string[] = [];\n\n lines.push('[Previous Conversation Summary]');\n\n // User requests\n if (summary.userRequests.length > 0) {\n lines.push('');\n lines.push('User Requests:');\n for (const req of summary.userRequests.slice(-5)) {\n lines.push(` - ${req.slice(0, 150)}${req.length > 150 ? '...' : ''}`);\n }\n }\n\n // Tool calls summary\n if (summary.toolCalls.length > 0) {\n lines.push('');\n lines.push('Tool Operations:');\n \n // Group by operation type\n const byType = new Map<string, ToolCallSummary[]>();\n for (const call of summary.toolCalls) {\n const list = byType.get(call.operation) || [];\n list.push(call);\n byType.set(call.operation, list);\n }\n\n for (const [type, calls] of byType.entries()) {\n const files = calls.filter(c => c.filePath).map(c => c.filePath!);\n const successCount = calls.filter(c => c.success).length;\n lines.push(` ${type.toUpperCase()}: ${calls.length} operations (${successCount}/${calls.length} successful)`);\n if (files.length > 0) {\n lines.push(` Files: ${files.slice(0, 10).join(', ')}${files.length > 10 ? '...' : ''}`);\n }\n }\n }\n\n // Files modified\n if (summary.filesModified.length > 0) {\n lines.push('');\n lines.push('Files Modified:');\n for (const file of summary.filesModified.slice(-10)) {\n lines.push(` - ${file}`);\n }\n }\n\n // Key decisions\n if (summary.keyDecisions.length > 0) {\n lines.push('');\n lines.push('Key Decisions:');\n for (const decision of summary.keyDecisions.slice(-5)) {\n lines.push(` - ${decision}`);\n }\n }\n\n // Usage statistics\n if (includeUsage) {\n lines.push('');\n lines.push('Statistics:');\n lines.push(` Total tool calls: ${summary.totalToolCalls}`);\n lines.push(` Successful: ${summary.successfulToolCalls}`);\n lines.push(` Failed: ${summary.failedToolCalls}`);\n lines.push(` Files modified: ${summary.filesModified.length}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Generate compact summary message for context injection\n */\nexport function createSummaryMessage(summary: ConversationSummary): AgentMessage {\n const text = formatSummaryAsText(summary, true);\n \n return {\n role: 'user',\n content: [{ type: 'text', text }],\n timestamp: Date.now(),\n };\n}\n"],"mappings":";;;;aAaqD;AAGrD,MAAM,MAAM,aAAa,mBAAmB;AA4B5C,MAAM,kBAA4C;CAChD,iBAAiB;CACjB,cAAc;CACd,mBAAmB;CACnB,cAAc;CACf;;;;AAKD,SAAS,wBAAwB,UAAmE;CAClG,MAAM,OAAO,SAAS,aAAa;AAEnC,KAAI;EAAC;EAAQ;EAAa;EAAiB;EAAQ;EAAY;EAAM;EAAa,CAAC,MAAK,MAAK,KAAK,SAAS,EAAE,CAAC,CAC5G,QAAO;AAGT,KAAI;EAAC;EAAS;EAAc;EAAkB;EAAQ;EAAS;EAAU;EAAQ,CAAC,MAAK,MAAK,KAAK,SAAS,EAAE,CAAC,CAC3G,QAAO;AAGT,KAAI;EAAC;EAAQ;EAAU;EAAQ;EAAc;EAAiB;EAAa,CAAC,MAAK,MAAK,KAAK,SAAS,EAAE,CAAC,CACrG,QAAO;AAGT,KAAI;EAAC;EAAQ;EAAS;EAAQ;EAAO;EAAU,CAAC,MAAK,MAAK,KAAK,SAAS,EAAE,CAAC,CACzE,QAAO;AAGT,QAAO;;;;;AAMT,SAAS,mBAAmB,SAA0B;AACpD,KAAI,OAAO,YAAY,SACrB,QAAO;AAGT,KAAI,MAAM,QAAQ,QAAQ,CACxB,QAAO,QACJ,QAAO,MAAK,EAAE,SAAS,OAAO,CAC9B,KAAI,MAAM,EAAwB,QAAQ,GAAG,CAC7C,KAAK,KAAK;AAGf,QAAO;;;;;AAMT,SAAS,aAAa,KAAoE;AACxF,QAAO,IAAI,SAAS,eAAe,eAAe;;;;;AAMpD,SAAS,aAAa,KAAgF;AACpG,QAAO,IAAI,SAAS,gBAAgB,UAAU;;;;;AAMhD,SAAS,sBAAsB,WAAiD;CAC9E,MAAM,yBAAS,IAAI,KAA8B;CACjD,MAAM,aAAgC,EAAE;AAExC,MAAK,MAAM,QAAQ,UACjB,KAAI,KAAK,SAEP,QAAO,IAAI,KAAK,UAAU,KAAK;KAG/B,YAAW,KAAK,KAAK;AAIzB,QAAO,CAAC,GAAG,MAAM,KAAK,OAAO,QAAQ,CAAC,EAAE,GAAG,WAAW;;;;;AAMxD,SAAgB,0BACd,UACA,UAA6C,EAAE,EAC1B;CACrB,MAAM,OAAO;EAAE,GAAG;EAAiB,GAAG;EAAS;CAE/C,MAAM,UAA+B;EACnC,cAAc,EAAE;EAChB,WAAW,EAAE;EACb,cAAc,EAAE;EAChB,eAAe,EAAE;EACjB,gBAAgB;EAChB,qBAAqB;EACrB,iBAAiB;EAClB;CAED,IAAI,uBAA4C;AAEhD,MAAK,MAAM,OAAO,UAAU;AAE1B,MAAI,IAAI,SAAS,QAAQ;GACvB,MAAM,OAAO,mBAAmB,wBAAwB,IAAI,CAAC;AAC7D,OAAI,KAAK,MAAM,CAAC,SAAS,GACvB,SAAQ,aAAa,KAAK,KAAK,MAAM,GAAG,IAAI,CAAC;;AAKjD,MAAI,aAAa,IAAI,EAAE;AACrB,0BAAuB;GAEvB,MAAM,YAAa,IAA0F;AAC7G,OAAI,aAAa,UAAU,SAAS,GAAG;AACrC,YAAQ,kBAAkB,UAAU;AAEpC,SAAK,MAAM,QAAQ,WAAW;KAC5B,MAAM,cAA+B;MACnC,UAAU,KAAK;MACf,WAAW,wBAAwB,KAAK,KAAK;MAC7C,SAAS;MACT,UAAU,OAAO,KAAK,MAAM,QAAQ,KAAK,MAAM,aAAa,KAAK,MAAM,YAAY,GAAG;MACtF,WAAW,IAAI;MAChB;AACD,aAAQ,UAAU,KAAK,YAAY;AAEnC,SAAI,CAAC,SAAS,OAAO,CAAC,SAAS,YAAY,UAAU,IAAI,YAAY,SACnE,SAAQ,cAAc,KAAK,YAAY,SAAS;;;;AAOxD,MAAI,aAAa,IAAI,EAAE;GACrB,MAAM,UAAU;GAChB,MAAM,WAAW,QAAQ;GAGzB,MAAM,WAAW,QAAQ,UACtB,OAAO,CACP,SAAS,CACT,MAAM,OAAwB,GAAG,aAAa,YAAY,CAAC,GAAG,UAAU;AAE3E,OAAI,UAAU;IACZ,MAAM,aAAa,mBAAmB,wBAAwB,QAAQ,CAAC;IAGvE,MAAM,UAAU,WAAW,aAAa,CAAC,SAAS,QAAQ,IAC3C,WAAW,aAAa,CAAC,SAAS,SAAS,IAC3C,WAAW,WAAW,SAAS;AAE9C,aAAS,UAAU,CAAC;AACpB,aAAS,cAAc,WAAW,MAAM,GAAG,IAAI;AAE/C,QAAI,QACF,SAAQ;QAER,SAAQ;;;;AAOhB,KAAI,KAAK,kBACP,SAAQ,YAAY,sBAAsB,QAAQ,UAAU;AAI9D,KAAI,QAAQ,UAAU,SAAS,KAAK,aAClC,SAAQ,YAAY,QAAQ,UAAU,MAAM,CAAC,KAAK,aAAa;AAIjE,KAAI,QAAQ,aAAa,SAAS,KAAK,gBACrC,SAAQ,eAAe,QAAQ,aAAa,MAAM,CAAC,KAAK,gBAAgB;AAI1E,SAAQ,gBAAgB,CAAC,GAAG,IAAI,IAAI,QAAQ,cAAc,CAAC;AAG3D,KAAI,qBAEF,SAAQ,eADU,oBAAoB,qBACN;AAGlC,KAAI,MACF;EACE,cAAc,QAAQ,aAAa;EACnC,WAAW,QAAQ,UAAU;EAC7B,eAAe,QAAQ,cAAc;EACrC,gBAAgB,QAAQ;EACxB,qBAAqB,QAAQ;EAC7B,iBAAiB,QAAQ;EAC1B,EACD,+BACD;AAED,QAAO;;;;;AAMT,SAAS,oBAAoB,KAA6B;CACxD,MAAM,UAAU,mBAAmB,wBAAwB,IAAI,CAAC;CAChE,MAAM,YAAsB,EAAE;AAS9B,MAAK,MAAM,WAAW;EALpB;EACA;EACA;EAGoC,EAAE;EACtC,MAAM,UAAU,QAAQ,MAAM,QAAQ;AACtC,MAAI,QACF,WAAU,KAAK,GAAG,QAAQ,MAAM,GAAG,EAAE,CAAC,KAAI,MAAK,EAAE,MAAM,CAAC,CAAC;;AAK7D,QAAO,UAAU,MAAM,GAAG,GAAG;;;;;AAM/B,SAAgB,oBAAoB,SAA8B,eAAwB,MAAc;CACtG,MAAM,QAAkB,EAAE;AAE1B,OAAM,KAAK,kCAAkC;AAG7C,KAAI,QAAQ,aAAa,SAAS,GAAG;AACnC,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,iBAAiB;AAC5B,OAAK,MAAM,OAAO,QAAQ,aAAa,MAAM,GAAG,CAC9C,OAAM,KAAK,OAAO,IAAI,MAAM,GAAG,IAAI,GAAG,IAAI,SAAS,MAAM,QAAQ,KAAK;;AAK1E,KAAI,QAAQ,UAAU,SAAS,GAAG;AAChC,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,mBAAmB;EAG9B,MAAM,yBAAS,IAAI,KAAgC;AACnD,OAAK,MAAM,QAAQ,QAAQ,WAAW;GACpC,MAAM,OAAO,OAAO,IAAI,KAAK,UAAU,IAAI,EAAE;AAC7C,QAAK,KAAK,KAAK;AACf,UAAO,IAAI,KAAK,WAAW,KAAK;;AAGlC,OAAK,MAAM,CAAC,MAAM,UAAU,OAAO,SAAS,EAAE;GAC5C,MAAM,QAAQ,MAAM,QAAO,MAAK,EAAE,SAAS,CAAC,KAAI,MAAK,EAAE,SAAU;GACjE,MAAM,eAAe,MAAM,QAAO,MAAK,EAAE,QAAQ,CAAC;AAClD,SAAM,KAAK,KAAK,KAAK,aAAa,CAAC,IAAI,MAAM,OAAO,eAAe,aAAa,GAAG,MAAM,OAAO,cAAc;AAC9G,OAAI,MAAM,SAAS,EACjB,OAAM,KAAK,cAAc,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,KAAK,GAAG,MAAM,SAAS,KAAK,QAAQ,KAAK;;;AAMhG,KAAI,QAAQ,cAAc,SAAS,GAAG;AACpC,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,kBAAkB;AAC7B,OAAK,MAAM,QAAQ,QAAQ,cAAc,MAAM,IAAI,CACjD,OAAM,KAAK,OAAO,OAAO;;AAK7B,KAAI,QAAQ,aAAa,SAAS,GAAG;AACnC,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,iBAAiB;AAC5B,OAAK,MAAM,YAAY,QAAQ,aAAa,MAAM,GAAG,CACnD,OAAM,KAAK,OAAO,WAAW;;AAKjC,KAAI,cAAc;AAChB,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,cAAc;AACzB,QAAM,KAAK,uBAAuB,QAAQ,iBAAiB;AAC3D,QAAM,KAAK,iBAAiB,QAAQ,sBAAsB;AAC1D,QAAM,KAAK,aAAa,QAAQ,kBAAkB;AAClD,QAAM,KAAK,qBAAqB,QAAQ,cAAc,SAAS;;AAGjE,QAAO,MAAM,KAAK,KAAK;;;;;AAMzB,SAAgB,qBAAqB,SAA4C;AAG/E,QAAO;EACL,MAAM;EACN,SAAS,CAAC;GAAE,MAAM;GAAQ,MAJf,oBAAoB,SAAS,KAIV;GAAE,CAAC;EACjC,WAAW,KAAK,KAAK;EACtB"}
1
+ {"version":3,"file":"summary-generator.js","names":[],"sources":["../../../../src/agent/memory/summary-generator.ts"],"sourcesContent":["/**\n * Summary Generator - Generates structured summaries for context compaction\n * \n * Creates structured summaries that preserve:\n * - User requests and intents\n * - Tool call history (deduplicated by file path)\n * - Key decisions and outcomes\n * - Modified files list\n * - Usage statistics\n */\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport type { ToolResultMessage, Message } from '@earendil-works/pi-ai';\nimport { createLogger } from '../../utils/logger.js';\nimport { readAgentMessageContent } from './agent-message-access.js';\n\nconst log = createLogger('SummaryGenerator');\n\nexport interface ToolCallSummary {\n toolName: string;\n filePath?: string;\n operation: 'read' | 'write' | 'search' | 'command' | 'other';\n success: boolean;\n briefResult?: string;\n timestamp?: number;\n}\n\nexport interface ConversationSummary {\n userRequests: string[]; // User's core requests\n toolCalls: ToolCallSummary[]; // Tool call history\n keyDecisions: string[]; // Key decisions made\n filesModified: string[]; // List of modified files\n totalToolCalls: number; // Total tool calls (before dedup)\n successfulToolCalls: number; // Successful tool calls\n failedToolCalls: number; // Failed tool calls\n}\n\nexport interface SummaryGenerationOptions {\n maxUserRequests: number; // Max user requests to keep (default: 10)\n maxToolCalls: number; // Max tool calls to keep after dedup (default: 50)\n deduplicateByFile: boolean; // Deduplicate tool calls by file path (default: true)\n includeUsage: boolean; // Include usage statistics (default: true)\n}\n\nconst DEFAULT_OPTIONS: SummaryGenerationOptions = {\n maxUserRequests: 10,\n maxToolCalls: 50,\n deduplicateByFile: true,\n includeUsage: true,\n};\n\n/**\n * Categorize tool operation based on tool name\n */\nfunction categorizeToolOperation(toolName: string): 'read' | 'write' | 'search' | 'command' | 'other' {\n const name = toolName.toLowerCase();\n \n if (['read', 'read_file', 'read_multiple', 'glob', 'list_dir', 'ls', 'memory_get'].some(n => name.includes(n))) {\n return 'read';\n }\n \n if (['write', 'write_file', 'write_multiple', 'edit', 'patch', 'create', 'mkdir'].some(n => name.includes(n))) {\n return 'write';\n }\n \n if (['grep', 'search', 'find', 'find_files', 'memory_search', 'web_search'].some(n => name.includes(n))) {\n return 'search';\n }\n \n if (['bash', 'exec', 'run', 'command'].some(n => name.includes(n))) {\n return 'command';\n }\n \n return 'other';\n}\n\n/**\n * Extract text content from message\n */\nfunction extractTextContent(content: unknown): string {\n if (typeof content === 'string') {\n return content;\n }\n \n if (Array.isArray(content)) {\n return content\n .filter(c => c.type === 'text')\n .map(c => (c as { text?: string }).text || '')\n .join('\\n');\n }\n \n return '';\n}\n\n/**\n * Type guard: Check if message has tool calls (assistant message)\n */\nfunction hasToolCalls(msg: AgentMessage): msg is AgentMessage & { toolCalls?: unknown[] } {\n return msg.role === 'assistant' && 'toolCalls' in msg;\n}\n\n/**\n * Type guard: Check if message is a tool result\n */\nfunction isToolResult(msg: AgentMessage): msg is AgentMessage & { name?: string; role: 'toolResult' } {\n return msg.role === 'toolResult' && 'name' in msg;\n}\n\n/**\n * Deduplicate tool calls by file path (keep last operation per file)\n */\nfunction deduplicateByFilePath(toolCalls: ToolCallSummary[]): ToolCallSummary[] {\n const byFile = new Map<string, ToolCallSummary>();\n const otherCalls: ToolCallSummary[] = [];\n\n for (const call of toolCalls) {\n if (call.filePath) {\n // Keep the last operation for each file\n byFile.set(call.filePath, call);\n } else {\n // Keep all non-file operations\n otherCalls.push(call);\n }\n }\n\n return [...Array.from(byFile.values()), ...otherCalls];\n}\n\n/**\n * Generate structured summary from messages\n */\nexport function generateStructuredSummary(\n messages: AgentMessage[],\n options: Partial<SummaryGenerationOptions> = {}\n): ConversationSummary {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n \n const summary: ConversationSummary = {\n userRequests: [],\n toolCalls: [],\n keyDecisions: [],\n filesModified: [],\n totalToolCalls: 0,\n successfulToolCalls: 0,\n failedToolCalls: 0,\n };\n\n let lastAssistantMessage: AgentMessage | null = null;\n\n for (const msg of messages) {\n // Extract user requests\n if (msg.role === 'user') {\n const text = extractTextContent(readAgentMessageContent(msg));\n if (text.trim().length > 20) {\n summary.userRequests.push(text.slice(0, 500)); // Limit each request length\n }\n }\n\n // Extract tool calls from assistant messages\n if (hasToolCalls(msg)) {\n lastAssistantMessage = msg;\n \n const toolCalls = (msg as Message & { toolCalls?: Array<{ name: string; args?: Record<string, unknown> }> }).toolCalls;\n if (toolCalls && toolCalls.length > 0) {\n summary.totalToolCalls += toolCalls.length;\n\n for (const call of toolCalls) {\n const toolSummary: ToolCallSummary = {\n toolName: call.name,\n operation: categorizeToolOperation(call.name),\n success: true, // Will be updated from tool_result\n filePath: String(call.args?.path || call.args?.file_path || call.args?.filePath || ''),\n timestamp: msg.timestamp,\n };\n summary.toolCalls.push(toolSummary);\n\n if (['write', 'edit'].includes(toolSummary.operation) && toolSummary.filePath) {\n summary.filesModified.push(toolSummary.filePath);\n }\n }\n }\n }\n\n // Extract tool results to determine success/failure\n if (isToolResult(msg)) {\n const toolMsg = msg as ToolResultMessage;\n const toolName = toolMsg.toolName;\n \n // Find the corresponding tool call and update success status\n const toolCall = summary.toolCalls\n .slice()\n .reverse()\n .find((tc: ToolCallSummary) => tc.toolName === toolName && !tc.timestamp); // Match by name, prefer unmatched\n \n if (toolCall) {\n const resultText = extractTextContent(readAgentMessageContent(toolMsg));\n \n // Detect failure from result content\n const isError = resultText.toLowerCase().includes('error') || \n resultText.toLowerCase().includes('failed') ||\n resultText.startsWith('Error:');\n \n toolCall.success = !isError;\n toolCall.briefResult = resultText.slice(0, 200);\n\n if (isError) {\n summary.failedToolCalls++;\n } else {\n summary.successfulToolCalls++;\n }\n }\n }\n }\n\n // Deduplicate tool calls by file path if enabled\n if (opts.deduplicateByFile) {\n summary.toolCalls = deduplicateByFilePath(summary.toolCalls);\n }\n\n // Limit tool calls\n if (summary.toolCalls.length > opts.maxToolCalls) {\n summary.toolCalls = summary.toolCalls.slice(-opts.maxToolCalls);\n }\n\n // Limit user requests\n if (summary.userRequests.length > opts.maxUserRequests) {\n summary.userRequests = summary.userRequests.slice(-opts.maxUserRequests);\n }\n\n // Deduplicate files modified\n summary.filesModified = [...new Set(summary.filesModified)];\n\n // Extract key decisions from last assistant message\n if (lastAssistantMessage) {\n const decisions = extractKeyDecisions(lastAssistantMessage);\n summary.keyDecisions = decisions;\n }\n\n log.debug(\n {\n userRequests: summary.userRequests.length,\n toolCalls: summary.toolCalls.length,\n filesModified: summary.filesModified.length,\n totalToolCalls: summary.totalToolCalls,\n successfulToolCalls: summary.successfulToolCalls,\n failedToolCalls: summary.failedToolCalls,\n },\n 'Generated structured summary'\n );\n\n return summary;\n}\n\n/**\n * Extract key decisions from assistant message\n */\nfunction extractKeyDecisions(msg: AgentMessage): string[] {\n const content = extractTextContent(readAgentMessageContent(msg));\n const decisions: string[] = [];\n\n // Look for decision patterns\n const decisionPatterns = [\n /(?:I'll|I will|Let's|We should|Decision:|Decided to|Chose to)\\s+(.+?)(?:\\.|\\n)/gi,\n /(?:Based on|After analyzing|Given that)\\s+(.+?)(?:\\.|\\n)/gi,\n /(?:The best approach|The solution|The answer) is\\s+(.+?)(?:\\.|\\n)/gi,\n ];\n\n for (const pattern of decisionPatterns) {\n const matches = content.match(pattern);\n if (matches) {\n decisions.push(...matches.slice(0, 5).map(m => m.trim()));\n }\n }\n\n // Limit decisions\n return decisions.slice(0, 10);\n}\n\n/**\n * Format structured summary as text for injection into context\n */\nexport function formatSummaryAsText(summary: ConversationSummary, includeUsage: boolean = true): string {\n const lines: string[] = [];\n\n lines.push('[Previous Conversation Summary]');\n\n // User requests\n if (summary.userRequests.length > 0) {\n lines.push('');\n lines.push('User Requests:');\n for (const req of summary.userRequests.slice(-5)) {\n lines.push(` - ${req.slice(0, 150)}${req.length > 150 ? '...' : ''}`);\n }\n }\n\n // Tool calls summary\n if (summary.toolCalls.length > 0) {\n lines.push('');\n lines.push('Tool Operations:');\n \n // Group by operation type\n const byType = new Map<string, ToolCallSummary[]>();\n for (const call of summary.toolCalls) {\n const list = byType.get(call.operation) || [];\n list.push(call);\n byType.set(call.operation, list);\n }\n\n for (const [type, calls] of byType.entries()) {\n const files = calls.filter(c => c.filePath).map(c => c.filePath!);\n const successCount = calls.filter(c => c.success).length;\n lines.push(` ${type.toUpperCase()}: ${calls.length} operations (${successCount}/${calls.length} successful)`);\n if (files.length > 0) {\n lines.push(` Files: ${files.slice(0, 10).join(', ')}${files.length > 10 ? '...' : ''}`);\n }\n }\n }\n\n // Files modified\n if (summary.filesModified.length > 0) {\n lines.push('');\n lines.push('Files Modified:');\n for (const file of summary.filesModified.slice(-10)) {\n lines.push(` - ${file}`);\n }\n }\n\n // Key decisions\n if (summary.keyDecisions.length > 0) {\n lines.push('');\n lines.push('Key Decisions:');\n for (const decision of summary.keyDecisions.slice(-5)) {\n lines.push(` - ${decision}`);\n }\n }\n\n // Usage statistics\n if (includeUsage) {\n lines.push('');\n lines.push('Statistics:');\n lines.push(` Total tool calls: ${summary.totalToolCalls}`);\n lines.push(` Successful: ${summary.successfulToolCalls}`);\n lines.push(` Failed: ${summary.failedToolCalls}`);\n lines.push(` Files modified: ${summary.filesModified.length}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Generate compact summary message for context injection\n */\nexport function createSummaryMessage(summary: ConversationSummary): AgentMessage {\n const text = formatSummaryAsText(summary, true);\n \n return {\n role: 'user',\n content: [{ type: 'text', text }],\n timestamp: Date.now(),\n };\n}\n"],"mappings":"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"}
@@ -2,5 +2,5 @@
2
2
  * Agent middleware — request-scoped log context (`ContextMiddleware`) and
3
3
  * harness-style self-verify tracking (`SelfVerifyMiddleware`).
4
4
  */
5
- export { SelfVerifyMiddleware, type FileEditRecord, type SelfVerifyConfig, } from './self-verify.js';
5
+ export { SelfVerifyMiddleware, type FileEditRecord, type SelfVerifyConfig, type SelfVerifyAgentKind, type SelfVerifyState, type VerificationOutcome, type VerificationRecord, } from './self-verify.js';
6
6
  export { ContextMiddleware, type RequestContext } from './context.js';
@@ -24,46 +24,73 @@ export interface SelfVerifyConfig {
24
24
  /** Reset counters on successful verification (default: true) */
25
25
  resetOnVerification: boolean;
26
26
  }
27
+ export interface VerificationOutcome {
28
+ isError?: boolean;
29
+ result?: unknown;
30
+ }
31
+ export interface VerificationRecord {
32
+ toolName: string;
33
+ command?: string;
34
+ success: boolean;
35
+ exitCode?: number | null;
36
+ status?: string;
37
+ timedOut?: boolean;
38
+ }
39
+ export interface SelfVerifyState {
40
+ hasUnverifiedEdits: boolean;
41
+ changedFiles: string[];
42
+ diffReviewed: boolean;
43
+ verificationAttempted: boolean;
44
+ lastVerificationFailed: boolean;
45
+ lastVerification?: VerificationRecord;
46
+ lastMutationTool?: string;
47
+ }
48
+ export type SelfVerifyAgentKind = 'coder' | 'data' | 'writer' | 'researcher' | 'creative' | 'generic';
27
49
  /**
28
50
  * Tracks file modifications and provides verification guidance
29
51
  */
30
52
  export declare class SelfVerifyMiddleware {
31
- private fileEdits;
53
+ private readonly states;
32
54
  private config;
33
- private turnCount;
34
- private verificationRequested;
35
- private hasUnverifiedEdits;
36
55
  constructor(config?: Partial<SelfVerifyConfig>);
56
+ private getState;
37
57
  /**
38
58
  * Record a file edit operation
39
59
  */
40
- recordEdit(filePath: string, operation: 'write' | 'edit' | 'shell'): void;
41
- recordVerification(toolName: string, args?: unknown): void;
60
+ recordEdit(filePath: string, operation: 'write' | 'edit' | 'command', sessionKey?: string): void;
61
+ recordVerification(toolName: string, args?: unknown, outcome?: VerificationOutcome, sessionKey?: string): void;
62
+ getVerificationState(sessionKey?: string): SelfVerifyState;
63
+ getPendingVerificationContext(sessionKey?: string, agentId?: string): string;
64
+ private extractExecCommand;
65
+ private extractResultDetails;
66
+ private isSuccessfulExecVerification;
67
+ private isVerificationCommand;
68
+ private isDiffReviewCommand;
42
69
  /**
43
70
  * Get edit count for a specific file
44
71
  */
45
- getEditCount(filePath: string): number;
72
+ getEditCount(filePath: string, sessionKey?: string): number;
46
73
  /**
47
74
  * Check if any file has excessive edits (potential doom loop)
48
75
  */
49
- hasExcessiveEdits(): {
76
+ hasExcessiveEdits(sessionKey?: string): {
50
77
  file: string;
51
78
  count: number;
52
79
  } | null;
53
80
  /**
54
81
  * Increment turn counter
55
82
  */
56
- onTurnStart(): void;
83
+ onTurnStart(sessionKey?: string): void;
57
84
  /**
58
85
  * Reset all tracking (e.g., after successful task completion)
59
86
  */
60
- reset(): void;
61
- consumePostEditReminder(): string;
87
+ reset(sessionKey?: string): void;
88
+ consumePostEditReminder(sessionKey?: string): string;
62
89
  /**
63
90
  * Get context injection for system prompt
64
91
  * Adds verification guidance based on current state
65
92
  */
66
- getContextInjection(): string;
93
+ getContextInjection(sessionKey?: string, agentId?: string): string;
67
94
  /**
68
95
  * Check if we should inject verification reminder
69
96
  */
@@ -71,11 +98,14 @@ export declare class SelfVerifyMiddleware {
71
98
  /**
72
99
  * Mark verification as requested (to avoid spam)
73
100
  */
74
- markVerificationRequested(): void;
101
+ markVerificationRequested(sessionKey?: string): void;
75
102
  /**
76
103
  * Build workflow guidance section
77
104
  */
78
105
  private buildWorkflowGuidance;
106
+ private classifyAgent;
107
+ private buildPendingVerificationReminder;
108
+ private verificationProfile;
79
109
  /**
80
110
  * Build warning for excessive file edits (doom loop detection)
81
111
  */
@@ -87,7 +117,7 @@ export declare class SelfVerifyMiddleware {
87
117
  /**
88
118
  * Get summary of file edits for debugging
89
119
  */
90
- getEditSummary(): {
120
+ getEditSummary(sessionKey?: string): {
91
121
  totalFiles: number;
92
122
  totalEdits: number;
93
123
  topFiles: Array<{