tarsk 0.6.87 → 0.6.88

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 (257) hide show
  1. package/dist/bundled-skills/email-me/SKILL.md +15 -0
  2. package/dist/bundled-skills/email-me/scripts/__tests__/email_me.test.js +64 -0
  3. package/dist/bundled-skills/email-me/scripts/email_me.js +45 -0
  4. package/dist/bundled-skills/handoff/SKILL.md +19 -0
  5. package/dist/bundled-skills/tarsk-agent/SKILL.md +372 -0
  6. package/dist/bundled-skills/tarsk-ask-user/SKILL.md +18 -0
  7. package/dist/bundled-skills/tarsk-ask-user/scripts/ask_user.js +101 -0
  8. package/dist/bundled-skills/tarsk-bash/SKILL.md +17 -0
  9. package/dist/bundled-skills/tarsk-bash/scripts/bash.js +248 -0
  10. package/dist/bundled-skills/tarsk-browser/SKILL.md +17 -0
  11. package/dist/bundled-skills/tarsk-browser/scripts/browser.js +252 -0
  12. package/dist/bundled-skills/tarsk-chat-title/SKILL.md +14 -0
  13. package/dist/bundled-skills/tarsk-chat-title/scripts/chat_title.js +37 -0
  14. package/dist/bundled-skills/tarsk-connect-mcp/SKILL.md +23 -0
  15. package/dist/bundled-skills/tarsk-connect-mcp/scripts/__tests__/connect_mcp.test.js +68 -0
  16. package/dist/bundled-skills/tarsk-connect-mcp/scripts/connect_mcp.js +119 -0
  17. package/dist/bundled-skills/tarsk-create-project/SKILL.md +25 -0
  18. package/dist/bundled-skills/tarsk-create-project/scripts/__tests__/create_project.test.js +94 -0
  19. package/dist/bundled-skills/tarsk-create-project/scripts/create_project.js +88 -0
  20. package/dist/bundled-skills/tarsk-edit/SKILL.md +18 -0
  21. package/dist/bundled-skills/tarsk-edit/scripts/edit.js +118 -0
  22. package/dist/bundled-skills/tarsk-execute-skill-script/SKILL.md +19 -0
  23. package/dist/bundled-skills/tarsk-execute-skill-script/scripts/execute_skill_script.js +77 -0
  24. package/dist/bundled-skills/tarsk-fetch/SKILL.md +18 -0
  25. package/dist/bundled-skills/tarsk-fetch/scripts/fetch.js +38 -0
  26. package/dist/bundled-skills/tarsk-find/SKILL.md +16 -0
  27. package/dist/bundled-skills/tarsk-find/scripts/find.js +78 -0
  28. package/dist/bundled-skills/tarsk-find-images/SKILL.md +18 -0
  29. package/dist/bundled-skills/tarsk-find-images/scripts/find_images.js +78 -0
  30. package/dist/bundled-skills/tarsk-generate-image/SKILL.md +18 -0
  31. package/dist/bundled-skills/tarsk-generate-image/scripts/generate_image.js +55 -0
  32. package/dist/bundled-skills/tarsk-get-observation/SKILL.md +19 -0
  33. package/dist/bundled-skills/tarsk-get-observation/scripts/get_observation.js +293 -0
  34. package/dist/bundled-skills/tarsk-grep/SKILL.md +16 -0
  35. package/dist/bundled-skills/tarsk-grep/scripts/grep.js +174 -0
  36. package/dist/bundled-skills/tarsk-history/SKILL.md +15 -0
  37. package/dist/bundled-skills/tarsk-history/scripts/history.js +68 -0
  38. package/dist/bundled-skills/tarsk-improve/SKILL.md +232 -0
  39. package/dist/bundled-skills/tarsk-improve/references/signals.md +16 -0
  40. package/dist/bundled-skills/tarsk-improve/scripts/durable-runs.ts +635 -0
  41. package/dist/bundled-skills/tarsk-improve/scripts/ensure-exported.ts +238 -0
  42. package/dist/bundled-skills/tarsk-improve/scripts/export-tool-skills-lib.ts +299 -0
  43. package/dist/bundled-skills/tarsk-improve/scripts/summarize-session.ts +126 -0
  44. package/dist/bundled-skills/tarsk-jobs/SKILL.md +18 -0
  45. package/dist/bundled-skills/tarsk-jobs/scripts/job_kill.js +45 -0
  46. package/dist/bundled-skills/tarsk-jobs/scripts/job_list.js +64 -0
  47. package/dist/bundled-skills/tarsk-jobs/scripts/job_output.js +66 -0
  48. package/dist/bundled-skills/tarsk-notes/SKILL.md +17 -0
  49. package/dist/bundled-skills/tarsk-notes/scripts/notes.js +86 -0
  50. package/dist/bundled-skills/tarsk-projects/SKILL.md +18 -0
  51. package/dist/bundled-skills/tarsk-projects/scripts/projects.js +54 -0
  52. package/dist/bundled-skills/tarsk-prompt-project/SKILL.md +18 -0
  53. package/dist/bundled-skills/tarsk-prompt-project/scripts/prompt_project.js +58 -0
  54. package/dist/bundled-skills/tarsk-propose-branch/SKILL.md +16 -0
  55. package/dist/bundled-skills/tarsk-propose-branch/scripts/propose_branch.js +45 -0
  56. package/dist/bundled-skills/tarsk-read/SKILL.md +17 -0
  57. package/dist/bundled-skills/tarsk-read/scripts/read.js +331 -0
  58. package/dist/bundled-skills/tarsk-read-skill/SKILL.md +16 -0
  59. package/dist/bundled-skills/tarsk-read-skill/scripts/read_skill.js +37 -0
  60. package/dist/bundled-skills/tarsk-read-skill-reference/SKILL.md +19 -0
  61. package/dist/bundled-skills/tarsk-read-skill-reference/scripts/read_skill_reference.js +60 -0
  62. package/dist/bundled-skills/tarsk-run-js/SKILL.md +18 -0
  63. package/dist/bundled-skills/tarsk-run-js/scripts/run_js.js +192 -0
  64. package/dist/bundled-skills/tarsk-tasks/SKILL.md +18 -0
  65. package/dist/bundled-skills/tarsk-tasks/scripts/tasks.js +96 -0
  66. package/dist/bundled-skills/tarsk-todo/SKILL.md +16 -0
  67. package/dist/bundled-skills/tarsk-todo/scripts/todo.js +165 -0
  68. package/dist/bundled-skills/tarsk-web-search/SKILL.md +15 -0
  69. package/dist/bundled-skills/tarsk-web-search/scripts/web_search.js +120 -0
  70. package/dist/bundled-skills/tarsk-write/SKILL.md +16 -0
  71. package/dist/bundled-skills/tarsk-write/scripts/write.js +57 -0
  72. package/dist/index.js +215804 -210026
  73. package/dist/node_modules/@libsql/darwin-arm64/README.md +3 -0
  74. package/dist/node_modules/@libsql/{darwin-x64 → darwin-arm64}/index.node +0 -0
  75. package/dist/node_modules/@libsql/{darwin-x64 → darwin-arm64}/package.json +6 -6
  76. package/dist/public/assets/account-view-DkuCOxkE.js +1 -0
  77. package/dist/public/assets/{alert-dialog-DVaEQlVa.js → alert-dialog-BcZULJsX.js} +1 -1
  78. package/dist/public/assets/api-B_GSyYGh.js +1 -0
  79. package/dist/public/assets/api-C4ukGdyH.js +2 -0
  80. package/dist/public/assets/{async-error-boundary-DH6Sqgmt.js → async-error-boundary-FT8_xvjy.js} +1 -1
  81. package/dist/public/assets/browser-tab-DCjHytQ8.js +12 -0
  82. package/dist/public/assets/button-DDaJhJP1.js +1 -0
  83. package/dist/public/assets/{card-BkOGFps5.js → card-DAc45J1v.js} +1 -1
  84. package/dist/public/assets/chat-dock-context-a2bV9cnz.js +1 -0
  85. package/dist/public/assets/chat-handlers-context-2xSKzkKa.js +9 -0
  86. package/dist/public/assets/commit-dialog-C1oUFznJ.js +1 -0
  87. package/dist/public/assets/{commit-error-dialog-D3S6kRVr.js → commit-error-dialog--NTHg7Aw.js} +1 -1
  88. package/dist/public/assets/{commit-method-fields-BEKfSRyW.js → commit-method-fields-Xt3vbwUg.js} +1 -1
  89. package/dist/public/assets/{conversation-history-view-CVMD8hUU.js → conversation-history-view-CdfE7JTD.js} +1 -1
  90. package/dist/public/assets/{create-project-dialog-nquqlloK.js → create-project-dialog-BdR6iQE9.js} +2 -2
  91. package/dist/public/assets/{create-project-workspace-C4tXAUWJ.js → create-project-workspace-DSTTGom_.js} +2 -2
  92. package/dist/public/assets/create-repo-dialog-gw-8Rpdm.js +1 -0
  93. package/dist/public/assets/{delete-confirm-dialog-SpYpU1AX.js → delete-confirm-dialog-D1N86piR.js} +1 -1
  94. package/dist/public/assets/dialog-D_12AsZd.js +1 -0
  95. package/dist/public/assets/diff-view-B6JJNyn1.js +4 -0
  96. package/dist/public/assets/{dist-NcyjV9Gu.js → dist-2xH_pFmV.js} +1 -1
  97. package/dist/public/assets/{dist-BTu7PDzc.js → dist-AbkJBHH5.js} +1 -1
  98. package/dist/public/assets/{dist-lFBinzor.js → dist-B-h6Wj39.js} +1 -1
  99. package/dist/public/assets/dist-CgbWuuq4.js +1 -0
  100. package/dist/public/assets/{dist-f54JuzFV.js → dist-DOz2WXwX.js} +1 -1
  101. package/dist/public/assets/{dist-CqrmQxmV.js → dist-DfnQ9YJh.js} +1 -1
  102. package/dist/public/assets/{dist-BOfRC57H.js → dist-JGbQ_EJY.js} +1 -1
  103. package/dist/public/assets/{dist-ButPmyjr.js → dist-pCVg1Jdl.js} +1 -1
  104. package/dist/public/assets/dist-v_MvKS2-.js +2 -0
  105. package/dist/public/assets/{es2015-CE7VcoOH.js → es2015-eNVkKfFF.js} +1 -1
  106. package/dist/public/assets/{explorer-file-drop-CEbBlDEp.js → explorer-file-drop-CzyAqZgf.js} +1 -1
  107. package/dist/public/assets/{explorer-monaco-editor-Bgo9Y1cP.js → explorer-monaco-editor-ChJZBFy5.js} +1 -1
  108. package/dist/public/assets/explorer-tab-view-CiUPh4uK.js +2 -0
  109. package/dist/public/assets/explorer-tree-BA12VLz_.js +1 -0
  110. package/dist/public/assets/{explorer-view-BuTa_0Uq.js → explorer-view-Daxl93oM.js} +3 -3
  111. package/dist/public/assets/git-history-dialog-Dqj1p35g.js +1 -0
  112. package/dist/public/assets/index-CoY8DilQ.js +97 -0
  113. package/dist/public/assets/index-D14wLW4o.css +1 -0
  114. package/dist/public/assets/{label-C31qF5zZ.js → label-BCs5CZEs.js} +1 -1
  115. package/dist/public/assets/markdown-renderer-D2VcKU4g.js +10 -0
  116. package/dist/public/assets/{mcp-manager-C3K_62H2.js → mcp-manager-D1HmLT3t.js} +1 -1
  117. package/dist/public/assets/{minus-l6BdrAEk.js → minus-CgQcbBQT.js} +1 -1
  118. package/dist/public/assets/{model-list-controls-DwW8S18v.js → model-list-controls-rXmaDp2r.js} +1 -1
  119. package/dist/public/assets/onboarding-dialog-DSqBJyBa.js +1 -0
  120. package/dist/public/assets/{pill-tabs-CmvvHdKG.js → pill-tabs-DtdTmrg7.js} +1 -1
  121. package/dist/public/assets/progress-indicator-hJ74ac5W.js +1 -0
  122. package/dist/public/assets/project-coding-view-DP4qKePE.js +1 -0
  123. package/dist/public/assets/{project-danger-zone-view-D18k4pUF.js → project-danger-zone-view-CGxIenpr.js} +1 -1
  124. package/dist/public/assets/{project-env-vars-view-BWFqoaFD.js → project-env-vars-view-DKyQkh7h.js} +1 -1
  125. package/dist/public/assets/project-model-aliases-view-BewwJZAx.js +1 -0
  126. package/dist/public/assets/project-settings-view-CE4nAz5B.js +1 -0
  127. package/dist/public/assets/{project-tools-view-DT87KPjE.js → project-tools-view-CbDfst02.js} +1 -1
  128. package/dist/public/assets/providers-list-view-BtwccvX2.js +1 -0
  129. package/dist/public/assets/pull-request-dialog-nKYATjbm.js +1 -0
  130. package/dist/public/assets/pull-with-changes-dialog-zzoFzCv4.js +1 -0
  131. package/dist/public/assets/push-before-pr-dialog-DH91JHEC.js +1 -0
  132. package/dist/public/assets/remote-media-url-CyuN31H_.js +1 -0
  133. package/dist/public/assets/{scroll-area-BWc4OPsj.js → scroll-area-CGEZ1gAL.js} +1 -1
  134. package/dist/public/assets/{select-B6RizwDW.js → select-BR2pG61R.js} +1 -1
  135. package/dist/public/assets/settings-general-tabs-view-BWR0UX2P.js +2 -0
  136. package/dist/public/assets/settings-general-view-BhtrDmJH.js +1 -0
  137. package/dist/public/assets/{settings-list-D35rpAof.js → settings-list-Dgip9HpD.js} +1 -1
  138. package/dist/public/assets/settings-logs-view-CS29x4VT.js +5 -0
  139. package/dist/public/assets/settings-mcp-servers-view-B5ithpfh.js +5 -0
  140. package/dist/public/assets/settings-models-view-DS7nm54P.js +1 -0
  141. package/dist/public/assets/settings-plugins-view-avaUE36r.js +2 -0
  142. package/dist/public/assets/settings-providers-view-B39k1iRQ.js +2 -0
  143. package/dist/public/assets/settings-remote-machines-view-BXondQG_.js +1 -0
  144. package/dist/public/assets/settings-setup-view-I5MGUi_D.js +2 -0
  145. package/dist/public/assets/settings-skills-view-BKrUABbi.js +2 -0
  146. package/dist/public/assets/{settings-slack-view-DkclyaBc.js → settings-slack-view-B0p-96GC.js} +1 -1
  147. package/dist/public/assets/settings-slash-commands-view-DA1scWv1.js +1 -0
  148. package/dist/public/assets/{settings-statistics-view-B03ZZjEy.js → settings-statistics-view-B3g6BD7p.js} +1 -1
  149. package/dist/public/assets/settings-storage-view-BJvjhI4f.js +1 -0
  150. package/dist/public/assets/settings-subagents-view-BziLwvNq.js +2 -0
  151. package/dist/public/assets/settings-system-prompt-view-BqAk56cD.js +1 -0
  152. package/dist/public/assets/{settings-tab-bar-ajzyt79U.js → settings-tab-bar-DYzU_lmi.js} +1 -1
  153. package/dist/public/assets/settings-view-zjMs7jNe.js +2 -0
  154. package/dist/public/assets/{slug-utils-D2jhPz-o.js → slug-utils-CO6nui1C.js} +1 -1
  155. package/dist/public/assets/square-CtxvwmM2.js +1 -0
  156. package/dist/public/assets/store-W78TGOIO.js +8 -0
  157. package/dist/public/assets/{table-C3Hc0Gwy.js → table-CQMqIW_-.js} +1 -1
  158. package/dist/public/assets/task-utils-BZPVmvY4.js +1 -0
  159. package/dist/public/assets/tasks-view-wFf77AQX.js +1 -0
  160. package/dist/public/assets/{terminal-panel-DCKqScJu.js → terminal-panel-Bm_ZelN-.js} +2 -2
  161. package/dist/public/assets/{toggle-group-DqSxGqvP.js → toggle-group-CPmseZfb.js} +1 -1
  162. package/dist/public/assets/{tooltip-CJKJfu0l.js → tooltip-CqwCZ7wt.js} +1 -1
  163. package/dist/public/assets/{use-account-settings-query-AUeOqKvo.js → use-account-settings-query-C-zOdREq.js} +1 -1
  164. package/dist/public/assets/{use-active-dock-panel-instance-BvAhUd_V.js → use-active-dock-panel-instance-4Ss8lB9K.js} +1 -1
  165. package/dist/public/assets/use-browser-overlay-block-CO1pUEAZ.js +1 -0
  166. package/dist/public/assets/use-git-check-CumqGAvN.js +1 -0
  167. package/dist/public/assets/{use-installed-ai-files-iy_AB0dN.js → use-installed-ai-files-DQey9Zm3.js} +1 -1
  168. package/dist/public/assets/use-open-chat-dock-panel-DqfWJ8e8.js +1 -0
  169. package/dist/public/assets/use-projects-CizIH2Fa.js +2 -0
  170. package/dist/public/assets/utils-CqsIXqSC.js +1 -0
  171. package/dist/public/assets/web-BNWoiS4w.js +1 -0
  172. package/dist/public/assets/web-CGP-jEpi.js +1 -0
  173. package/dist/public/assets/web-CKC9gpFi.js +1 -0
  174. package/dist/public/assets/web-Dlo3GwCf.js +1 -0
  175. package/dist/public/fonts/schibsted-grotesk-latin-ext-wght-normal.woff2 +0 -0
  176. package/dist/public/fonts/schibsted-grotesk-latin-wght-normal.woff2 +0 -0
  177. package/dist/public/index.html +45 -45
  178. package/package.json +6 -6
  179. package/dist/node_modules/@libsql/darwin-x64/README.md +0 -3
  180. package/dist/public/assets/account-view-5q3pHEHY.js +0 -1
  181. package/dist/public/assets/ai-spinner-DHwhlk4j.js +0 -1
  182. package/dist/public/assets/api-BdebJfuN.js +0 -1
  183. package/dist/public/assets/api-COO2LfdU.js +0 -2
  184. package/dist/public/assets/browser-overlay-block-CiDtkfup.js +0 -1
  185. package/dist/public/assets/browser-tab-CVdYLmW5.js +0 -12
  186. package/dist/public/assets/button-Dp5XDZNR.js +0 -1
  187. package/dist/public/assets/chat-dock-context-ymLoO90X.js +0 -1
  188. package/dist/public/assets/checkbox-D-BV8-Z_.js +0 -1
  189. package/dist/public/assets/commit-dialog-CEbCFh1x.js +0 -1
  190. package/dist/public/assets/create-repo-dialog-B5IE3pUJ.js +0 -1
  191. package/dist/public/assets/dialog-pHPeIGaB.js +0 -1
  192. package/dist/public/assets/diff-view-Cy6nPdX-.js +0 -4
  193. package/dist/public/assets/dist-D5vgS4RP.js +0 -2
  194. package/dist/public/assets/explorer-tab-view-C-8QZO9S.js +0 -2
  195. package/dist/public/assets/explorer-tree-HrLSFgcI.js +0 -1
  196. package/dist/public/assets/git-history-dialog-ClfF5IOn.js +0 -1
  197. package/dist/public/assets/index-BBLA5zVt.css +0 -1
  198. package/dist/public/assets/index-CalVAwXj.js +0 -104
  199. package/dist/public/assets/markdown-renderer-DXRBG7uP.js +0 -10
  200. package/dist/public/assets/onboarding-dialog-BtLy0wjC.js +0 -1
  201. package/dist/public/assets/progress-indicator-4zQXqOqP.js +0 -1
  202. package/dist/public/assets/project-coding-view-CyJwnJes.js +0 -1
  203. package/dist/public/assets/project-model-aliases-view-D3tYNkkM.js +0 -1
  204. package/dist/public/assets/project-settings-view-0oN9vFvD.js +0 -1
  205. package/dist/public/assets/providers-list-view-DCnh0W-5.js +0 -1
  206. package/dist/public/assets/pull-request-dialog-BdJxYQwu.js +0 -1
  207. package/dist/public/assets/pull-with-changes-dialog-Do9x9neW.js +0 -1
  208. package/dist/public/assets/push-before-pr-dialog-C-9z2O0-.js +0 -1
  209. package/dist/public/assets/remote-media-url-B8OFqxOM.js +0 -1
  210. package/dist/public/assets/settings-general-tabs-view-Dcek-ysB.js +0 -2
  211. package/dist/public/assets/settings-general-view-DhA2N6G5.js +0 -1
  212. package/dist/public/assets/settings-logs-view-eEUTIMeb.js +0 -5
  213. package/dist/public/assets/settings-mcp-servers-view-_oeuVkUe.js +0 -5
  214. package/dist/public/assets/settings-models-view-DW-ZmVup.js +0 -1
  215. package/dist/public/assets/settings-plugins-view-DTooco_c.js +0 -2
  216. package/dist/public/assets/settings-providers-view-CEeJkhg9.js +0 -2
  217. package/dist/public/assets/settings-remote-machines-view--UB8GlCK.js +0 -1
  218. package/dist/public/assets/settings-setup-view-DFL6DlYS.js +0 -2
  219. package/dist/public/assets/settings-skills-view-xS569-Kb.js +0 -2
  220. package/dist/public/assets/settings-slash-commands-view-DrCiayfq.js +0 -1
  221. package/dist/public/assets/settings-storage-view-CQnaVOt_.js +0 -1
  222. package/dist/public/assets/settings-subagents-view-DIctUJcE.js +0 -2
  223. package/dist/public/assets/settings-system-prompt-view-BMDbrFRR.js +0 -1
  224. package/dist/public/assets/settings-view-8M6sbGiX.js +0 -2
  225. package/dist/public/assets/store-CxUwqrZ5.js +0 -8
  226. package/dist/public/assets/task-utils-WAei2PDi.js +0 -1
  227. package/dist/public/assets/tasks-view-DKyqgGdE.js +0 -1
  228. package/dist/public/assets/use-browser-overlay-block-z9imTmiZ.js +0 -1
  229. package/dist/public/assets/use-git-check-B_5Jak2s.js +0 -1
  230. package/dist/public/assets/use-open-chat-dock-panel-w3hc5Vxn.js +0 -1
  231. package/dist/public/assets/use-projects-BmhAMgxB.js +0 -2
  232. package/dist/public/assets/utils-mYB1USG6.js +0 -1
  233. package/dist/public/assets/web-C6Gm4nQ0.js +0 -1
  234. package/dist/public/assets/web-DFlyNg8o.js +0 -1
  235. package/dist/public/assets/web-DlEPs8Xe.js +0 -1
  236. package/dist/public/assets/web-Gx3xReQH.js +0 -1
  237. package/dist/public/backgrounds/arctic-tundra.avif +0 -0
  238. package/dist/public/backgrounds/autumn-leaves.avif +0 -0
  239. package/dist/public/backgrounds/coastal-cliff.avif +0 -0
  240. package/dist/public/backgrounds/floating-waterfall.avif +0 -0
  241. package/dist/public/backgrounds/forest-path.avif +0 -0
  242. package/dist/public/backgrounds/garden-path.avif +0 -0
  243. package/dist/public/backgrounds/glow-marsh.avif +0 -0
  244. package/dist/public/backgrounds/golden-wheat.avif +0 -0
  245. package/dist/public/backgrounds/moon-desert.avif +0 -0
  246. package/dist/public/backgrounds/night-pond.avif +0 -0
  247. package/dist/public/backgrounds/prairie-grassland.avif +0 -0
  248. package/dist/public/backgrounds/river-winding.avif +0 -0
  249. package/dist/public/backgrounds/salt-flat.avif +0 -0
  250. package/dist/public/backgrounds/salt-marsh.avif +0 -0
  251. package/dist/public/backgrounds/sandy-beach.avif +0 -0
  252. package/dist/public/backgrounds/snow-river.avif +0 -0
  253. package/dist/public/backgrounds/sunset-pond.avif +0 -0
  254. package/dist/public/fonts/plus-jakarta-sans-cyrillic-ext-wght-normal.woff2 +0 -0
  255. package/dist/public/fonts/plus-jakarta-sans-latin-ext-wght-normal.woff2 +0 -0
  256. package/dist/public/fonts/plus-jakarta-sans-latin-wght-normal.woff2 +0 -0
  257. package/dist/public/fonts/plus-jakarta-sans-vietnamese-wght-normal.woff2 +0 -0
@@ -0,0 +1,635 @@
1
+ // @ts-nocheck — bun script outside cli/tsconfig (no Node/Bun types in staged vp check)
2
+ /**
3
+ * Shared reader for tarsk-improve (and compatible with the learn skill helpers).
4
+ *
5
+ * Reads agent runs straight from Tarsk's durable conversation store in
6
+ * `tarsk.db` (pi-durable `entries` table) — no log file or JSONL export needed.
7
+ *
8
+ * A **run** is one `pi.user` entry plus everything the agent did before the
9
+ * next `pi.user` entry in the same durable conversation: model calls
10
+ * (`pi.assistant`), tool results (`pi.tool-result`), and context resets
11
+ * (`pi.reset`).
12
+ *
13
+ * The database is opened read-only, so it is safe while Tarsk is running.
14
+ */
15
+
16
+ import { Database } from "bun:sqlite";
17
+ import { existsSync } from "node:fs";
18
+ import { homedir } from "node:os";
19
+ import { join, resolve } from "node:path";
20
+
21
+ export const DEFAULT_SCAN = 300;
22
+ export const DEFAULT_LIMIT = 8;
23
+
24
+ const CONTINUATION_PROMPT_RE = /^You stopped before finishing the current task/i;
25
+ const ABORTED_RE = /was aborted/i;
26
+ const DURATION_UNITS_MS: Record<string, number> = {
27
+ m: 60_000,
28
+ h: 3_600_000,
29
+ d: 86_400_000,
30
+ };
31
+
32
+ /** Paths and tools tied to project agent config — excluded from scoring. */
33
+ const PROJECT_CONFIG_PATH_RE =
34
+ /(?:\.agents\/(?:skills|rules|slash-commands)|\/SKILL\.md|AGENTS\.md|\.cursor\/rules|slash-commands)/i;
35
+
36
+ const PROJECT_CONFIG_TOOLS = new Set([
37
+ "read_skill",
38
+ "read_skill_reference",
39
+ "execute_skill_script",
40
+ ]);
41
+
42
+ export interface ToolCall {
43
+ id: string;
44
+ name: string;
45
+ args: Record<string, unknown>;
46
+ /** `name` + serialized args, for spotting identical repeats. */
47
+ signature: string;
48
+ /** Size of the stored tool-result record, in bytes. Undefined until a result arrives. */
49
+ resultBytes?: number;
50
+ /** True when the tool returned an error that was not a user abort. */
51
+ isError: boolean;
52
+ /** True when the user (or a crash/restart) aborted the call. */
53
+ aborted: boolean;
54
+ /** First part of the tool result text, kept for failures only. */
55
+ errorText?: string;
56
+ }
57
+
58
+ export interface Run {
59
+ entryId: number;
60
+ durableConversationId: number;
61
+ threadId?: string;
62
+ conversationId?: string;
63
+ /** Set when this durable conversation is a child (subagent) of another. */
64
+ ownerConversationId?: number;
65
+ startedAt?: string;
66
+ startedAtMs?: number;
67
+ durationSec?: number;
68
+ prompt: string;
69
+ /** True for the automatic "You stopped before finishing…" nudge. */
70
+ continuation: boolean;
71
+ model?: string;
72
+ provider?: string;
73
+ modelCalls: number;
74
+ /** Counts by assistant stopReason (stop, toolUse, error, aborted, length…). */
75
+ stopReasons: Map<string, number>;
76
+ providerErrors: number;
77
+ calls: ToolCall[];
78
+ resets: number;
79
+ /** Per-model-call context size: input + cacheRead + cacheWrite tokens. */
80
+ contextTokens: number[];
81
+ uncachedInputTokens: number;
82
+ cacheReadTokens: number;
83
+ cacheWriteTokens: number;
84
+ outputTokens: number;
85
+ reasoningTokens: number;
86
+ costTotal: number;
87
+ }
88
+
89
+ export interface CommonArgs {
90
+ dbPath: string;
91
+ scan: number;
92
+ limit: number;
93
+ sinceMs?: number;
94
+ thread?: string;
95
+ conversation?: string;
96
+ }
97
+
98
+ interface UserRow {
99
+ id: number;
100
+ cid: number;
101
+ content: unknown;
102
+ ts: number | null;
103
+ thread_id: string | null;
104
+ tarsk_conversation_id: string | null;
105
+ owner: number | null;
106
+ }
107
+
108
+ interface EntryRow {
109
+ id: number;
110
+ kind: string;
111
+ bytes: number;
112
+ ts: number | null;
113
+ usage: string | null;
114
+ stop: string | null;
115
+ err: string | null;
116
+ model: string | null;
117
+ provider: string | null;
118
+ callId: string | null;
119
+ toolName: string | null;
120
+ isError: number | null;
121
+ text: string | null;
122
+ blocks: string | null;
123
+ }
124
+
125
+ interface RunBoundaries {
126
+ startsByConversation: Map<number, number[]>;
127
+ rootByUserEntry: Map<number, number>;
128
+ }
129
+
130
+ export function readFlagValue(args: string[], flag: string): string | undefined {
131
+ const index = args.indexOf(flag);
132
+ if (index === -1) return undefined;
133
+ const value = args[index + 1];
134
+ if (!value || value.startsWith("--")) return undefined;
135
+ return value;
136
+ }
137
+
138
+ export function truncate(text: string, max: number): string {
139
+ if (text.length <= max) return text;
140
+ return `${text.slice(0, max - 1)}…`;
141
+ }
142
+
143
+ export function formatToolHistogram(tools: Map<string, number>): string {
144
+ return (
145
+ [...tools.entries()]
146
+ .sort((a, b) => b[1] - a[1])
147
+ .map(([name, count]) => `${name}:${count}`)
148
+ .join(", ") || "(none)"
149
+ );
150
+ }
151
+
152
+ export function formatBytes(bytes: number): string {
153
+ if (bytes < 1024) return `${bytes}B`;
154
+ return `${(bytes / 1024).toFixed(1)}KB`;
155
+ }
156
+
157
+ /** `24h`, `7d`, `90m`, or an ISO date/time. Returns epoch ms. */
158
+ export function parseSince(value: string, now = Date.now()): number | undefined {
159
+ const match = value.match(/^(\d+)\s*([mhd])$/i);
160
+ if (match) {
161
+ return now - Number(match[1]) * DURATION_UNITS_MS[match[2].toLowerCase()];
162
+ }
163
+ const parsed = Date.parse(value);
164
+ return Number.isFinite(parsed) ? parsed : undefined;
165
+ }
166
+
167
+ /**
168
+ * Where Tarsk keeps `tarsk.db`. Mirrors `cli/src/core/paths.ts`:
169
+ * `TARSK_DATA_DIR`, else `<TARSK_APP_SUPPORT_DIR or ~/Library/Application Support/Tarsk>/data`.
170
+ */
171
+ export function resolveDbPath(flag?: string): string {
172
+ if (flag) return resolve(flag);
173
+ const dataDir =
174
+ process.env.TARSK_DATA_DIR ??
175
+ join(
176
+ process.env.TARSK_APP_SUPPORT_DIR ??
177
+ join(homedir(), "Library", "Application Support", "Tarsk"),
178
+ "data",
179
+ );
180
+ return join(dataDir, "tarsk.db");
181
+ }
182
+
183
+ function parsePositiveInt(value: string | undefined, fallback: number): number {
184
+ const parsed = Number.parseInt(value ?? "", 10);
185
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
186
+ }
187
+
188
+ export const COMMON_USAGE_FLAGS = `Options:
189
+ --db <path> tarsk.db to read (default: Tarsk data dir)
190
+ --since <24h|7d|ISO> Only runs that started after this
191
+ --thread <threadId> Only runs from this Tarsk thread
192
+ --conversation <id> Only runs from this Tarsk conversation
193
+ --scan <N> Newest N runs to load (default ${DEFAULT_SCAN})
194
+ --limit <N> Runs to print (default ${DEFAULT_LIMIT})`;
195
+
196
+ export function parseCommonArgs(args: string[]): CommonArgs {
197
+ const since = readFlagValue(args, "--since");
198
+ return {
199
+ dbPath: resolveDbPath(readFlagValue(args, "--db")),
200
+ scan: parsePositiveInt(readFlagValue(args, "--scan"), DEFAULT_SCAN),
201
+ limit: parsePositiveInt(readFlagValue(args, "--limit"), DEFAULT_LIMIT),
202
+ sinceMs: since ? parseSince(since) : undefined,
203
+ thread: readFlagValue(args, "--thread"),
204
+ conversation: readFlagValue(args, "--conversation"),
205
+ };
206
+ }
207
+
208
+ /** Open `tarsk.db` read-only. Exits with a clear message when it is missing. */
209
+ export function openDb(dbPath: string): Database {
210
+ if (!existsSync(dbPath)) {
211
+ console.error(`No database at ${dbPath}. Pass --db <path> or set TARSK_DATA_DIR.`);
212
+ process.exit(1);
213
+ }
214
+ return new Database(dbPath, { readonly: true });
215
+ }
216
+
217
+ /** Tarsk prepends this wrapper to every prompt; it is noise for review. */
218
+ const RUNTIME_CONTEXT_RE = /<runtime_context>[\s\S]*?<\/runtime_context>\s*/g;
219
+
220
+ function userPromptText(content: unknown): string {
221
+ if (typeof content !== "string") return "";
222
+ let text = content;
223
+ if (content.startsWith("[")) {
224
+ try {
225
+ const blocks = JSON.parse(content) as Array<{ type?: string; text?: string }>;
226
+ text = blocks
227
+ .filter((block) => block.type === "text" && typeof block.text === "string")
228
+ .map((block) => block.text)
229
+ .join("\n");
230
+ } catch {
231
+ text = content;
232
+ }
233
+ }
234
+ return text.replace(RUNTIME_CONTEXT_RE, "").trim();
235
+ }
236
+
237
+ function parseJson<T>(raw: string | null): T | undefined {
238
+ if (!raw) return undefined;
239
+ try {
240
+ return JSON.parse(raw) as T;
241
+ } catch {
242
+ return undefined;
243
+ }
244
+ }
245
+
246
+ function isoOrUndefined(ms: number | null | undefined): string | undefined {
247
+ return typeof ms === "number" && Number.isFinite(ms) ? new Date(ms).toISOString() : undefined;
248
+ }
249
+
250
+ interface AssistantUsage {
251
+ input?: number;
252
+ output?: number;
253
+ cacheRead?: number;
254
+ cacheWrite?: number;
255
+ reasoning?: number;
256
+ cost?: { total?: number };
257
+ }
258
+
259
+ function createRun(user: UserRow): Run {
260
+ const prompt = userPromptText(user.content);
261
+ return {
262
+ entryId: user.id,
263
+ durableConversationId: user.cid,
264
+ threadId: user.thread_id ?? undefined,
265
+ conversationId: user.tarsk_conversation_id ?? undefined,
266
+ ownerConversationId: user.owner ?? undefined,
267
+ startedAt: isoOrUndefined(user.ts),
268
+ startedAtMs: user.ts ?? undefined,
269
+ prompt,
270
+ continuation: CONTINUATION_PROMPT_RE.test(prompt),
271
+ modelCalls: 0,
272
+ stopReasons: new Map(),
273
+ providerErrors: 0,
274
+ calls: [],
275
+ resets: 0,
276
+ contextTokens: [],
277
+ uncachedInputTokens: 0,
278
+ cacheReadTokens: 0,
279
+ cacheWriteTokens: 0,
280
+ outputTokens: 0,
281
+ reasoningTokens: 0,
282
+ costTotal: 0,
283
+ };
284
+ }
285
+
286
+ function addAssistantEntry(run: Run, row: EntryRow, callsById: Map<string, ToolCall>): void {
287
+ run.modelCalls += 1;
288
+ if (row.model) run.model = row.model;
289
+ if (row.provider) run.provider = row.provider;
290
+
291
+ const stop = row.stop ?? "unknown";
292
+ run.stopReasons.set(stop, (run.stopReasons.get(stop) ?? 0) + 1);
293
+ if (stop === "error") run.providerErrors += 1;
294
+
295
+ const usage = parseJson<AssistantUsage>(row.usage);
296
+ if (usage) {
297
+ const input = usage.input ?? 0;
298
+ const cacheRead = usage.cacheRead ?? 0;
299
+ const cacheWrite = usage.cacheWrite ?? 0;
300
+ run.contextTokens.push(input + cacheRead + cacheWrite);
301
+ run.uncachedInputTokens += input;
302
+ run.cacheReadTokens += cacheRead;
303
+ run.cacheWriteTokens += cacheWrite;
304
+ run.outputTokens += usage.output ?? 0;
305
+ run.reasoningTokens += usage.reasoning ?? 0;
306
+ run.costTotal += usage.cost?.total ?? 0;
307
+ }
308
+
309
+ const blocks = parseJson<Array<Record<string, unknown>>>(row.blocks) ?? [];
310
+ for (const block of blocks) {
311
+ if (block.type !== "toolCall" || typeof block.name !== "string") continue;
312
+ const args =
313
+ block.arguments && typeof block.arguments === "object"
314
+ ? (block.arguments as Record<string, unknown>)
315
+ : {};
316
+ const call: ToolCall = {
317
+ id: typeof block.id === "string" ? block.id : `${row.id}:${run.calls.length}`,
318
+ name: block.name,
319
+ args,
320
+ signature: `${block.name}${JSON.stringify(args)}`,
321
+ isError: false,
322
+ aborted: false,
323
+ };
324
+ run.calls.push(call);
325
+ callsById.set(call.id, call);
326
+ }
327
+ }
328
+
329
+ function addToolResultEntry(row: EntryRow, callsById: Map<string, ToolCall>): void {
330
+ const call = row.callId ? callsById.get(row.callId) : undefined;
331
+ if (!call) return;
332
+
333
+ call.resultBytes = row.bytes;
334
+ const failed = row.isError === 1;
335
+ const aborted = failed && ABORTED_RE.test(row.text ?? "");
336
+ call.aborted = aborted;
337
+ call.isError = failed && !aborted;
338
+ if (failed) call.errorText = row.text ?? "";
339
+ }
340
+
341
+ function fillRunFromEntries(run: Run, rows: EntryRow[]): void {
342
+ const callsById = new Map<string, ToolCall>();
343
+ let lastTs = run.startedAtMs;
344
+
345
+ for (const row of rows) {
346
+ if (typeof row.ts === "number") lastTs = row.ts;
347
+ if (row.kind === "pi.assistant") addAssistantEntry(run, row, callsById);
348
+ else if (row.kind === "pi.tool-result") addToolResultEntry(row, callsById);
349
+ else if (row.kind === "pi.reset") run.resets += 1;
350
+ }
351
+
352
+ if (run.startedAtMs !== undefined && lastTs !== undefined && lastTs >= run.startedAtMs) {
353
+ run.durationSec = Math.round((lastTs - run.startedAtMs) / 1000);
354
+ }
355
+ }
356
+
357
+ const SELECT_ENTRY_ROWS = `
358
+ SELECT
359
+ id,
360
+ json_extract(record, '$.kind') AS kind,
361
+ length(record) AS bytes,
362
+ json_extract(record, '$.model[0].timestamp') AS ts,
363
+ json_extract(record, '$.model[0].usage') AS usage,
364
+ json_extract(record, '$.model[0].stopReason') AS stop,
365
+ json_extract(record, '$.model[0].errorMessage') AS err,
366
+ json_extract(record, '$.model[0].model') AS model,
367
+ json_extract(record, '$.model[0].provider') AS provider,
368
+ json_extract(record, '$.model[0].toolCallId') AS callId,
369
+ json_extract(record, '$.model[0].toolName') AS toolName,
370
+ json_extract(record, '$.model[0].isError') AS isError,
371
+ CASE WHEN json_extract(record, '$.kind') = 'pi.tool-result'
372
+ THEN substr(json_extract(record, '$.model[0].content[0].text'), 1, 1200) END AS text,
373
+ CASE WHEN json_extract(record, '$.kind') = 'pi.assistant'
374
+ THEN json_extract(record, '$.model[0].content') END AS blocks
375
+ FROM entries
376
+ WHERE conversation_id = ? AND id > ? AND id < ?
377
+ ORDER BY id
378
+ `;
379
+
380
+ const SELECT_USER_ROWS = `
381
+ SELECT
382
+ e.id AS id,
383
+ e.conversation_id AS cid,
384
+ json_extract(e.record, '$.model[0].content') AS content,
385
+ json_extract(e.record, '$.model[0].timestamp') AS ts,
386
+ s.thread_id AS thread_id,
387
+ s.conversation_id AS tarsk_conversation_id,
388
+ c.owner_conversation_id AS owner
389
+ FROM entries e
390
+ LEFT JOIN agent_durable_conversations a ON a.durable_conversation_id = e.conversation_id
391
+ LEFT JOIN agent_sessions s ON s.id = a.session_id
392
+ LEFT JOIN conversations c ON c.id = e.conversation_id
393
+ WHERE json_extract(e.record, '$.kind') = 'pi.user'
394
+ AND (? IS NULL OR s.thread_id = ?)
395
+ AND (? IS NULL OR s.conversation_id = ?)
396
+ AND (? IS NULL OR json_extract(e.record, '$.model[0].timestamp') >= ?)
397
+ ORDER BY e.id DESC
398
+ LIMIT ?
399
+ `;
400
+
401
+ /** Actual user prompts start runs; continuation nudges stay inside their prior run. */
402
+ function loadRunBoundaries(db: Database, conversationIds: number[]): RunBoundaries {
403
+ const startsByConversation = new Map<number, number[]>();
404
+ const rootByUserEntry = new Map<number, number>();
405
+ if (conversationIds.length === 0) return { startsByConversation, rootByUserEntry };
406
+
407
+ const placeholders = conversationIds.map(() => "?").join(",");
408
+ const rows = db
409
+ .query(
410
+ `SELECT id, conversation_id AS cid,
411
+ json_extract(record, '$.model[0].content') AS content
412
+ FROM entries
413
+ WHERE conversation_id IN (${placeholders})
414
+ AND json_extract(record, '$.kind') = 'pi.user'
415
+ ORDER BY id`,
416
+ )
417
+ .all(...conversationIds) as Array<{ id: number; cid: number; content: unknown }>;
418
+ const latestRunByConversation = new Map<number, number>();
419
+
420
+ for (const row of rows) {
421
+ const prompt = userPromptText(row.content);
422
+ const continuation = CONTINUATION_PROMPT_RE.test(prompt);
423
+ const latestRun = latestRunByConversation.get(row.cid);
424
+
425
+ if (continuation && latestRun !== undefined) {
426
+ rootByUserEntry.set(row.id, latestRun);
427
+ continue;
428
+ }
429
+
430
+ const ids = startsByConversation.get(row.cid) ?? [];
431
+ ids.push(row.id);
432
+ startsByConversation.set(row.cid, ids);
433
+ rootByUserEntry.set(row.id, row.id);
434
+ if (!continuation) latestRunByConversation.set(row.cid, row.id);
435
+ }
436
+
437
+ return { startsByConversation, rootByUserEntry };
438
+ }
439
+
440
+ function nextBoundary(ids: number[] | undefined, entryId: number): number {
441
+ const next = ids?.find((id) => id > entryId);
442
+ return next ?? Number.MAX_SAFE_INTEGER;
443
+ }
444
+
445
+ function loadUserRowById(db: Database, entryId: number): UserRow | undefined {
446
+ return db
447
+ .query(
448
+ `SELECT
449
+ e.id AS id,
450
+ e.conversation_id AS cid,
451
+ json_extract(e.record, '$.model[0].content') AS content,
452
+ json_extract(e.record, '$.model[0].timestamp') AS ts,
453
+ s.thread_id AS thread_id,
454
+ s.conversation_id AS tarsk_conversation_id,
455
+ c.owner_conversation_id AS owner
456
+ FROM entries e
457
+ LEFT JOIN agent_durable_conversations a ON a.durable_conversation_id = e.conversation_id
458
+ LEFT JOIN agent_sessions s ON s.id = a.session_id
459
+ LEFT JOIN conversations c ON c.id = e.conversation_id
460
+ WHERE e.id = ? AND json_extract(e.record, '$.kind') = 'pi.user'`,
461
+ )
462
+ .get(entryId) as UserRow | undefined;
463
+ }
464
+
465
+ /**
466
+ * Load the newest runs (up to `scan`), newest first.
467
+ * Runs whose prompt is the automatic continuation nudge are included and flagged.
468
+ */
469
+ export function loadRuns(db: Database, args: CommonArgs): Run[] {
470
+ const users = db
471
+ .query(SELECT_USER_ROWS)
472
+ .all(
473
+ args.thread ?? null,
474
+ args.thread ?? null,
475
+ args.conversation ?? null,
476
+ args.conversation ?? null,
477
+ args.sinceMs ?? null,
478
+ args.sinceMs ?? null,
479
+ args.scan,
480
+ ) as UserRow[];
481
+
482
+ const boundaries = loadRunBoundaries(db, [...new Set(users.map((user) => user.cid))]);
483
+ const selectedUsersById = new Map(users.map((user) => [user.id, user]));
484
+ const runStarts = new Map<number, UserRow>();
485
+
486
+ for (const user of users) {
487
+ const rootId = boundaries.rootByUserEntry.get(user.id) ?? user.id;
488
+ const root =
489
+ selectedUsersById.get(rootId) ?? (rootId === user.id ? user : loadUserRowById(db, rootId));
490
+ if (!root || (args.sinceMs !== undefined && root.ts !== null && root.ts < args.sinceMs)) {
491
+ continue;
492
+ }
493
+ runStarts.set(root.id, root);
494
+ }
495
+
496
+ const entryQuery = db.query(SELECT_ENTRY_ROWS);
497
+ return [...runStarts.values()]
498
+ .sort((a, b) => b.id - a.id)
499
+ .map((user) => {
500
+ const run = createRun(user);
501
+ const upper = nextBoundary(boundaries.startsByConversation.get(user.cid), user.id);
502
+ const rows = entryQuery.all(user.cid, user.id, upper) as EntryRow[];
503
+ fillRunFromEntries(run, rows);
504
+ return run;
505
+ });
506
+ }
507
+
508
+ /** Convenience for the summarizers: parse flags, open the DB, load runs, log provenance. */
509
+ export function loadRunsFromArgs(argv: string[]): { runs: Run[]; args: CommonArgs } {
510
+ const args = parseCommonArgs(argv);
511
+ const db = openDb(args.dbPath);
512
+ try {
513
+ const runs = loadRuns(db, args);
514
+ console.error(`Database: ${args.dbPath}`);
515
+ console.error(`Runs loaded: ${runs.length} (newest first, scan=${args.scan})\n`);
516
+ return { runs, args };
517
+ } finally {
518
+ db.close();
519
+ }
520
+ }
521
+
522
+ export function isProjectConfigToolCall(call: ToolCall): boolean {
523
+ if (PROJECT_CONFIG_TOOLS.has(call.name)) return true;
524
+
525
+ const path = typeof call.args.path === "string" ? call.args.path : "";
526
+ if (path && PROJECT_CONFIG_PATH_RE.test(path)) return true;
527
+
528
+ const command = typeof call.args.command === "string" ? call.args.command : "";
529
+ if (command && PROJECT_CONFIG_PATH_RE.test(command)) return true;
530
+
531
+ const pattern = typeof call.args.pattern === "string" ? call.args.pattern : "";
532
+ return pattern === "**/SKILL.md";
533
+ }
534
+
535
+ export function toolHistogram(
536
+ calls: ToolCall[],
537
+ options: { exclProjectConfig?: boolean } = {},
538
+ ): Map<string, number> {
539
+ const histogram = new Map<string, number>();
540
+ for (const call of calls) {
541
+ if (options.exclProjectConfig && isProjectConfigToolCall(call)) continue;
542
+ histogram.set(call.name, (histogram.get(call.name) ?? 0) + 1);
543
+ }
544
+ return histogram;
545
+ }
546
+
547
+ export function failedCalls(calls: ToolCall[]): ToolCall[] {
548
+ return calls.filter((call) => call.isError);
549
+ }
550
+
551
+ export function abortedCallCount(calls: ToolCall[]): number {
552
+ return calls.filter((call) => call.aborted).length;
553
+ }
554
+
555
+ /** Cleaned tool-error text: harness wrapper tags and the `[error]` prefix removed. */
556
+ export function failureDetail(call: ToolCall, max = 160): string {
557
+ const cleaned = (call.errorText ?? "failed")
558
+ .replace(/<\/?harness>/g, "")
559
+ .replace(/<\/?observation_preview[^>]*>/g, "")
560
+ .replace(/\[error\]\s*/g, "")
561
+ .replace(/\s+/g, " ")
562
+ .trim();
563
+ return truncate(cleaned || "failed", max);
564
+ }
565
+
566
+ /** Calls whose name+args already appeared earlier in the run (project config excluded). */
567
+ export function repeatedCallCount(calls: ToolCall[]): number {
568
+ const seen = new Set<string>();
569
+ let repeats = 0;
570
+ for (const call of calls) {
571
+ if (isProjectConfigToolCall(call)) continue;
572
+ if (seen.has(call.signature)) repeats += 1;
573
+ seen.add(call.signature);
574
+ }
575
+ return repeats;
576
+ }
577
+
578
+ /** Calls that re-ran the exact name+args of an earlier failed call. */
579
+ export function retriesAfterFailureCount(calls: ToolCall[]): number {
580
+ const failed = new Set<string>();
581
+ let retries = 0;
582
+ for (const call of calls) {
583
+ if (failed.has(call.signature)) retries += 1;
584
+ if (call.isError) failed.add(call.signature);
585
+ }
586
+ return retries;
587
+ }
588
+
589
+ export function largestResults(calls: ToolCall[], count = 3): string {
590
+ const sized = calls
591
+ .filter((call) => typeof call.resultBytes === "number" && !isProjectConfigToolCall(call))
592
+ .sort((a, b) => (b.resultBytes ?? 0) - (a.resultBytes ?? 0))
593
+ .slice(0, count);
594
+ return sized.map((call) => `${call.name}:${formatBytes(call.resultBytes ?? 0)}`).join(", ");
595
+ }
596
+
597
+ export function cacheReadPercent(run: Run): number | undefined {
598
+ const total = run.uncachedInputTokens + run.cacheReadTokens + run.cacheWriteTokens;
599
+ return total > 0 ? Math.round((run.cacheReadTokens / total) * 100) : undefined;
600
+ }
601
+
602
+ export function formatContextTrend(run: Run): string {
603
+ const tokens = run.contextTokens;
604
+ if (tokens.length === 0) return "n/a";
605
+ const peak = Math.max(...tokens);
606
+ return `${tokens[0]} → ${tokens[tokens.length - 1]} (peak ${peak}, ${tokens.length} calls)`;
607
+ }
608
+
609
+ export function formatStopReasons(run: Run): string {
610
+ return [...run.stopReasons.entries()].map(([reason, count]) => `${reason}:${count}`).join(", ");
611
+ }
612
+
613
+ /** Lines shared by every summarizer's run header. */
614
+ export function printRunHeader(run: Run, promptMax = 120): void {
615
+ console.log(`- started: ${run.startedAt ?? "?"}`);
616
+ console.log(`- thread: ${run.threadId ?? "?"}`);
617
+ console.log(`- conversation: ${run.conversationId ?? "?"}`);
618
+ console.log(
619
+ `- durable conversation: ${run.durableConversationId} (entry ${run.entryId})${
620
+ run.ownerConversationId === undefined ? "" : ` — subagent of ${run.ownerConversationId}`
621
+ }`,
622
+ );
623
+ console.log(`- model: ${run.model ?? "?"}${run.provider ? ` (${run.provider})` : ""}`);
624
+ console.log(
625
+ `- prompt: ${truncate(run.prompt.replace(/\s+/g, " "), promptMax) || "?"}${
626
+ run.continuation ? " [auto-continue nudge]" : ""
627
+ }`,
628
+ );
629
+ }
630
+
631
+ export function printNoRunsHint(): void {
632
+ console.log(
633
+ "No agent runs found. Check --db, widen --since / --scan, or confirm an agent has run in this Tarsk data dir.",
634
+ );
635
+ }