pi-ui-extend 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (420) hide show
  1. package/README.md +307 -0
  2. package/bin/pix.mjs +219 -0
  3. package/dist/app/app.d.ts +96 -0
  4. package/dist/app/app.js +871 -0
  5. package/dist/app/blink-controller.d.ts +23 -0
  6. package/dist/app/blink-controller.js +82 -0
  7. package/dist/app/cli.d.ts +8 -0
  8. package/dist/app/cli.js +83 -0
  9. package/dist/app/clipboard.d.ts +1 -0
  10. package/dist/app/clipboard.js +24 -0
  11. package/dist/app/command-controller.d.ts +18 -0
  12. package/dist/app/command-controller.js +58 -0
  13. package/dist/app/command-host.d.ts +44 -0
  14. package/dist/app/command-host.js +1 -0
  15. package/dist/app/command-model-actions.d.ts +12 -0
  16. package/dist/app/command-model-actions.js +176 -0
  17. package/dist/app/command-navigation-actions.d.ts +19 -0
  18. package/dist/app/command-navigation-actions.js +267 -0
  19. package/dist/app/command-registry.d.ts +32 -0
  20. package/dist/app/command-registry.js +225 -0
  21. package/dist/app/command-runtime.d.ts +5 -0
  22. package/dist/app/command-runtime.js +32 -0
  23. package/dist/app/command-session-actions.d.ts +20 -0
  24. package/dist/app/command-session-actions.js +295 -0
  25. package/dist/app/constants.d.ts +52 -0
  26. package/dist/app/constants.js +103 -0
  27. package/dist/app/conversation-entry-renderer.d.ts +21 -0
  28. package/dist/app/conversation-entry-renderer.js +81 -0
  29. package/dist/app/conversation-shell-renderer.d.ts +5 -0
  30. package/dist/app/conversation-shell-renderer.js +43 -0
  31. package/dist/app/conversation-tool-renderer.d.ts +16 -0
  32. package/dist/app/conversation-tool-renderer.js +216 -0
  33. package/dist/app/conversation-viewport.d.ts +55 -0
  34. package/dist/app/conversation-viewport.js +252 -0
  35. package/dist/app/dcp-stats.d.ts +2 -0
  36. package/dist/app/dcp-stats.js +116 -0
  37. package/dist/app/editor-layout-renderer.d.ts +31 -0
  38. package/dist/app/editor-layout-renderer.js +211 -0
  39. package/dist/app/editor-panels.d.ts +4 -0
  40. package/dist/app/editor-panels.js +130 -0
  41. package/dist/app/extension-actions-controller.d.ts +22 -0
  42. package/dist/app/extension-actions-controller.js +60 -0
  43. package/dist/app/extension-event-bus.d.ts +3 -0
  44. package/dist/app/extension-event-bus.js +23 -0
  45. package/dist/app/extension-ui-controller.d.ts +57 -0
  46. package/dist/app/extension-ui-controller.js +532 -0
  47. package/dist/app/file-link-opener.d.ts +2 -0
  48. package/dist/app/file-link-opener.js +66 -0
  49. package/dist/app/file-links.d.ts +10 -0
  50. package/dist/app/file-links.js +117 -0
  51. package/dist/app/guards.d.ts +3 -0
  52. package/dist/app/guards.js +9 -0
  53. package/dist/app/icons.d.ts +37 -0
  54. package/dist/app/icons.js +97 -0
  55. package/dist/app/id.d.ts +1 -0
  56. package/dist/app/id.js +4 -0
  57. package/dist/app/image-click-targets.d.ts +5 -0
  58. package/dist/app/image-click-targets.js +32 -0
  59. package/dist/app/image-opener.d.ts +2 -0
  60. package/dist/app/image-opener.js +64 -0
  61. package/dist/app/input-action-controller.d.ts +47 -0
  62. package/dist/app/input-action-controller.js +209 -0
  63. package/dist/app/input-controller.d.ts +60 -0
  64. package/dist/app/input-controller.js +425 -0
  65. package/dist/app/input-paste-handler.d.ts +27 -0
  66. package/dist/app/input-paste-handler.js +146 -0
  67. package/dist/app/menu-items-controller.d.ts +32 -0
  68. package/dist/app/menu-items-controller.js +182 -0
  69. package/dist/app/message-content.d.ts +8 -0
  70. package/dist/app/message-content.js +115 -0
  71. package/dist/app/model-ref.d.ts +13 -0
  72. package/dist/app/model-ref.js +50 -0
  73. package/dist/app/model-usage-controller.d.ts +35 -0
  74. package/dist/app/model-usage-controller.js +99 -0
  75. package/dist/app/model-usage-status.d.ts +125 -0
  76. package/dist/app/model-usage-status.js +749 -0
  77. package/dist/app/mouse-controller.d.ts +182 -0
  78. package/dist/app/mouse-controller.js +968 -0
  79. package/dist/app/native-modifiers.d.ts +3 -0
  80. package/dist/app/native-modifiers.js +60 -0
  81. package/dist/app/nerd-font-controller.d.ts +11 -0
  82. package/dist/app/nerd-font-controller.js +90 -0
  83. package/dist/app/popup-action-controller.d.ts +44 -0
  84. package/dist/app/popup-action-controller.js +278 -0
  85. package/dist/app/popup-menu-controller.d.ts +183 -0
  86. package/dist/app/popup-menu-controller.js +1070 -0
  87. package/dist/app/prompt-enhancer-controller.d.ts +40 -0
  88. package/dist/app/prompt-enhancer-controller.js +215 -0
  89. package/dist/app/queued-message-controller.d.ts +62 -0
  90. package/dist/app/queued-message-controller.js +377 -0
  91. package/dist/app/render-controller.d.ts +41 -0
  92. package/dist/app/render-controller.js +378 -0
  93. package/dist/app/render-text.d.ts +19 -0
  94. package/dist/app/render-text.js +67 -0
  95. package/dist/app/request-history.d.ts +23 -0
  96. package/dist/app/request-history.js +131 -0
  97. package/dist/app/runtime.d.ts +39 -0
  98. package/dist/app/runtime.js +195 -0
  99. package/dist/app/screen-selection.d.ts +6 -0
  100. package/dist/app/screen-selection.js +9 -0
  101. package/dist/app/screen-styler.d.ts +34 -0
  102. package/dist/app/screen-styler.js +168 -0
  103. package/dist/app/scroll-controller.d.ts +51 -0
  104. package/dist/app/scroll-controller.js +207 -0
  105. package/dist/app/session-event-controller.d.ts +69 -0
  106. package/dist/app/session-event-controller.js +338 -0
  107. package/dist/app/session-history.d.ts +23 -0
  108. package/dist/app/session-history.js +164 -0
  109. package/dist/app/session-lifecycle-controller.d.ts +55 -0
  110. package/dist/app/session-lifecycle-controller.js +116 -0
  111. package/dist/app/session-search.d.ts +24 -0
  112. package/dist/app/session-search.js +215 -0
  113. package/dist/app/shell-command.d.ts +27 -0
  114. package/dist/app/shell-command.js +176 -0
  115. package/dist/app/shell-controller.d.ts +28 -0
  116. package/dist/app/shell-controller.js +124 -0
  117. package/dist/app/slash-commands.d.ts +6 -0
  118. package/dist/app/slash-commands.js +75 -0
  119. package/dist/app/startup-checks.d.ts +8 -0
  120. package/dist/app/startup-checks.js +59 -0
  121. package/dist/app/startup-info.d.ts +3 -0
  122. package/dist/app/startup-info.js +176 -0
  123. package/dist/app/status-controller.d.ts +35 -0
  124. package/dist/app/status-controller.js +105 -0
  125. package/dist/app/status-line-renderer.d.ts +68 -0
  126. package/dist/app/status-line-renderer.js +453 -0
  127. package/dist/app/subagents-files.d.ts +10 -0
  128. package/dist/app/subagents-files.js +193 -0
  129. package/dist/app/subagents-model.d.ts +23 -0
  130. package/dist/app/subagents-model.js +224 -0
  131. package/dist/app/subagents-widget-controller.d.ts +43 -0
  132. package/dist/app/subagents-widget-controller.js +311 -0
  133. package/dist/app/tab-line-renderer.d.ts +26 -0
  134. package/dist/app/tab-line-renderer.js +222 -0
  135. package/dist/app/tabs-controller.d.ts +100 -0
  136. package/dist/app/tabs-controller.js +885 -0
  137. package/dist/app/terminal-controller.d.ts +40 -0
  138. package/dist/app/terminal-controller.js +135 -0
  139. package/dist/app/terminal-edit-shortcuts.d.ts +23 -0
  140. package/dist/app/terminal-edit-shortcuts.js +138 -0
  141. package/dist/app/terminal-output-buffer.d.ts +20 -0
  142. package/dist/app/terminal-output-buffer.js +52 -0
  143. package/dist/app/toast-controller.d.ts +13 -0
  144. package/dist/app/toast-controller.js +40 -0
  145. package/dist/app/toast-renderer.d.ts +9 -0
  146. package/dist/app/toast-renderer.js +79 -0
  147. package/dist/app/todo-model.d.ts +22 -0
  148. package/dist/app/todo-model.js +179 -0
  149. package/dist/app/todo-widget-controller.d.ts +21 -0
  150. package/dist/app/todo-widget-controller.js +59 -0
  151. package/dist/app/tool-block-renderer.d.ts +26 -0
  152. package/dist/app/tool-block-renderer.js +439 -0
  153. package/dist/app/types.d.ts +550 -0
  154. package/dist/app/types.js +1 -0
  155. package/dist/app/update.d.ts +36 -0
  156. package/dist/app/update.js +315 -0
  157. package/dist/app/voice-controller.d.ts +52 -0
  158. package/dist/app/voice-controller.js +600 -0
  159. package/dist/app/workspace-actions-controller.d.ts +40 -0
  160. package/dist/app/workspace-actions-controller.js +215 -0
  161. package/dist/app/workspace-undo.d.ts +44 -0
  162. package/dist/app/workspace-undo.js +215 -0
  163. package/dist/config.d.ts +62 -0
  164. package/dist/config.js +527 -0
  165. package/dist/context-progress-bar.d.ts +17 -0
  166. package/dist/context-progress-bar.js +48 -0
  167. package/dist/default-pix-config.d.ts +1 -0
  168. package/dist/default-pix-config.js +375 -0
  169. package/dist/fuzzy.d.ts +23 -0
  170. package/dist/fuzzy.js +165 -0
  171. package/dist/input-editor-files.d.ts +15 -0
  172. package/dist/input-editor-files.js +62 -0
  173. package/dist/input-editor.d.ts +186 -0
  174. package/dist/input-editor.js +835 -0
  175. package/dist/main.d.ts +1 -0
  176. package/dist/main.js +12 -0
  177. package/dist/markdown-format.d.ts +22 -0
  178. package/dist/markdown-format.js +542 -0
  179. package/dist/sdk.d.ts +3 -0
  180. package/dist/sdk.js +1 -0
  181. package/dist/syntax-highlight.d.ts +22 -0
  182. package/dist/syntax-highlight.js +528 -0
  183. package/dist/terminal-width.d.ts +6 -0
  184. package/dist/terminal-width.js +245 -0
  185. package/dist/theme.d.ts +56 -0
  186. package/dist/theme.js +118 -0
  187. package/dist/tool-renderers/apply-patch.d.ts +2 -0
  188. package/dist/tool-renderers/apply-patch.js +36 -0
  189. package/dist/tool-renderers/ast.d.ts +2 -0
  190. package/dist/tool-renderers/ast.js +5 -0
  191. package/dist/tool-renderers/compress.d.ts +2 -0
  192. package/dist/tool-renderers/compress.js +126 -0
  193. package/dist/tool-renderers/index.d.ts +3 -0
  194. package/dist/tool-renderers/index.js +44 -0
  195. package/dist/tool-renderers/question.d.ts +2 -0
  196. package/dist/tool-renderers/question.js +88 -0
  197. package/dist/tool-renderers/read.d.ts +2 -0
  198. package/dist/tool-renderers/read.js +36 -0
  199. package/dist/tool-renderers/repo.d.ts +2 -0
  200. package/dist/tool-renderers/repo.js +13 -0
  201. package/dist/tool-renderers/shell.d.ts +3 -0
  202. package/dist/tool-renderers/shell.js +27 -0
  203. package/dist/tool-renderers/skill.d.ts +2 -0
  204. package/dist/tool-renderers/skill.js +132 -0
  205. package/dist/tool-renderers/subagents.d.ts +2 -0
  206. package/dist/tool-renderers/subagents.js +51 -0
  207. package/dist/tool-renderers/todo.d.ts +2 -0
  208. package/dist/tool-renderers/todo.js +9 -0
  209. package/dist/tool-renderers/types.d.ts +42 -0
  210. package/dist/tool-renderers/types.js +1 -0
  211. package/dist/tool-renderers/utils.d.ts +31 -0
  212. package/dist/tool-renderers/utils.js +230 -0
  213. package/dist/tool-renderers/web.d.ts +3 -0
  214. package/dist/tool-renderers/web.js +9 -0
  215. package/dist/tool-renderers/write.d.ts +2 -0
  216. package/dist/tool-renderers/write.js +42 -0
  217. package/dist/ui.d.ts +56 -0
  218. package/dist/ui.js +94 -0
  219. package/docs/release.md +81 -0
  220. package/extensions/question/contract.ts +100 -0
  221. package/extensions/question/index.ts +34 -0
  222. package/extensions/question/render.ts +28 -0
  223. package/extensions/question/result.ts +86 -0
  224. package/extensions/question/tool-description.ts +11 -0
  225. package/extensions/question/tui.ts +629 -0
  226. package/extensions/question/types.ts +123 -0
  227. package/extensions/session-title/config.ts +169 -0
  228. package/extensions/session-title/index.ts +459 -0
  229. package/extensions/terminal-bell/index.ts +315 -0
  230. package/external/pi-tools-suite/README.md +242 -0
  231. package/external/pi-tools-suite/index.ts +1 -0
  232. package/external/pi-tools-suite/licenses/ollama-pi-web-search.MIT +21 -0
  233. package/external/pi-tools-suite/licenses/opencode-mystatus-MIT.txt +21 -0
  234. package/external/pi-tools-suite/package.json +53 -0
  235. package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +194 -0
  236. package/external/pi-tools-suite/src/antigravity-auth/commands.ts +80 -0
  237. package/external/pi-tools-suite/src/antigravity-auth/constants.ts +26 -0
  238. package/external/pi-tools-suite/src/antigravity-auth/headers.ts +20 -0
  239. package/external/pi-tools-suite/src/antigravity-auth/index.ts +104 -0
  240. package/external/pi-tools-suite/src/antigravity-auth/models.ts +86 -0
  241. package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +305 -0
  242. package/external/pi-tools-suite/src/antigravity-auth/payload.ts +423 -0
  243. package/external/pi-tools-suite/src/antigravity-auth/status.ts +78 -0
  244. package/external/pi-tools-suite/src/antigravity-auth/stream.ts +302 -0
  245. package/external/pi-tools-suite/src/antigravity-auth/types.ts +113 -0
  246. package/external/pi-tools-suite/src/ast-grep/index.ts +6 -0
  247. package/external/pi-tools-suite/src/ast-grep/render.ts +70 -0
  248. package/external/pi-tools-suite/src/ast-grep/schema.ts +109 -0
  249. package/external/pi-tools-suite/src/ast-grep/tool.ts +345 -0
  250. package/external/pi-tools-suite/src/ast-grep/types.ts +55 -0
  251. package/external/pi-tools-suite/src/ast-grep/utils.ts +65 -0
  252. package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +222 -0
  253. package/external/pi-tools-suite/src/async-subagents/commands.ts +518 -0
  254. package/external/pi-tools-suite/src/async-subagents/constants.ts +11 -0
  255. package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +74 -0
  256. package/external/pi-tools-suite/src/async-subagents/core/attachment-bridge.ts +133 -0
  257. package/external/pi-tools-suite/src/async-subagents/core/cleanup.ts +66 -0
  258. package/external/pi-tools-suite/src/async-subagents/core/concurrency.ts +90 -0
  259. package/external/pi-tools-suite/src/async-subagents/core/config.ts +819 -0
  260. package/external/pi-tools-suite/src/async-subagents/core/log-limits.ts +166 -0
  261. package/external/pi-tools-suite/src/async-subagents/core/model-fallback.ts +133 -0
  262. package/external/pi-tools-suite/src/async-subagents/core/paths.ts +47 -0
  263. package/external/pi-tools-suite/src/async-subagents/core/pi-invocation.ts +35 -0
  264. package/external/pi-tools-suite/src/async-subagents/core/presets.ts +67 -0
  265. package/external/pi-tools-suite/src/async-subagents/core/process.ts +15 -0
  266. package/external/pi-tools-suite/src/async-subagents/core/prompt.ts +66 -0
  267. package/external/pi-tools-suite/src/async-subagents/core/registry.ts +224 -0
  268. package/external/pi-tools-suite/src/async-subagents/core/retry.ts +191 -0
  269. package/external/pi-tools-suite/src/async-subagents/core/routing.ts +259 -0
  270. package/external/pi-tools-suite/src/async-subagents/core/sessions.ts +138 -0
  271. package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +688 -0
  272. package/external/pi-tools-suite/src/async-subagents/core/state.ts +281 -0
  273. package/external/pi-tools-suite/src/async-subagents/core/stop.ts +131 -0
  274. package/external/pi-tools-suite/src/async-subagents/core/structured-result.ts +237 -0
  275. package/external/pi-tools-suite/src/async-subagents/core/tool-guard.ts +34 -0
  276. package/external/pi-tools-suite/src/async-subagents/core/types.ts +150 -0
  277. package/external/pi-tools-suite/src/async-subagents/core/ultrawork-auto.ts +184 -0
  278. package/external/pi-tools-suite/src/async-subagents/core/utils.ts +11 -0
  279. package/external/pi-tools-suite/src/async-subagents/format.ts +41 -0
  280. package/external/pi-tools-suite/src/async-subagents/index.ts +422 -0
  281. package/external/pi-tools-suite/src/async-subagents/lib.ts +88 -0
  282. package/external/pi-tools-suite/src/async-subagents/live.ts +10 -0
  283. package/external/pi-tools-suite/src/async-subagents/polling.ts +83 -0
  284. package/external/pi-tools-suite/src/async-subagents/render.ts +230 -0
  285. package/external/pi-tools-suite/src/async-subagents/subagent-overlay.ts +77 -0
  286. package/external/pi-tools-suite/src/async-subagents/tasks.ts +120 -0
  287. package/external/pi-tools-suite/src/async-subagents/tools/cleanup.ts +99 -0
  288. package/external/pi-tools-suite/src/async-subagents/tools/result.ts +179 -0
  289. package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +372 -0
  290. package/external/pi-tools-suite/src/async-subagents/tools/status.ts +60 -0
  291. package/external/pi-tools-suite/src/async-subagents/tools/stop.ts +79 -0
  292. package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +152 -0
  293. package/external/pi-tools-suite/src/async-subagents/tools/wait.ts +67 -0
  294. package/external/pi-tools-suite/src/async-subagents/types.ts +45 -0
  295. package/external/pi-tools-suite/src/async-subagents/ui.ts +5 -0
  296. package/external/pi-tools-suite/src/compress/commands.ts +440 -0
  297. package/external/pi-tools-suite/src/compress/compress-tool.ts +368 -0
  298. package/external/pi-tools-suite/src/compress/compression-blocks.ts +524 -0
  299. package/external/pi-tools-suite/src/compress/config.ts +310 -0
  300. package/external/pi-tools-suite/src/compress/dcp-tui-filter.ts +498 -0
  301. package/external/pi-tools-suite/src/compress/index.ts +397 -0
  302. package/external/pi-tools-suite/src/compress/prompts.ts +269 -0
  303. package/external/pi-tools-suite/src/compress/pruner-candidates.ts +176 -0
  304. package/external/pi-tools-suite/src/compress/pruner-compression-blocks.ts +260 -0
  305. package/external/pi-tools-suite/src/compress/pruner-message-ids.ts +147 -0
  306. package/external/pi-tools-suite/src/compress/pruner-metadata.ts +268 -0
  307. package/external/pi-tools-suite/src/compress/pruner-nudge.ts +315 -0
  308. package/external/pi-tools-suite/src/compress/pruner-tools.ts +263 -0
  309. package/external/pi-tools-suite/src/compress/pruner-types.ts +25 -0
  310. package/external/pi-tools-suite/src/compress/pruner.ts +92 -0
  311. package/external/pi-tools-suite/src/compress/state.ts +486 -0
  312. package/external/pi-tools-suite/src/compress/ui.ts +308 -0
  313. package/external/pi-tools-suite/src/config.ts +176 -0
  314. package/external/pi-tools-suite/src/context-usage.ts +31 -0
  315. package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +355 -0
  316. package/external/pi-tools-suite/src/index.ts +46 -0
  317. package/external/pi-tools-suite/src/lib/lsp.ts +62 -0
  318. package/external/pi-tools-suite/src/lib/project.ts +42 -0
  319. package/external/pi-tools-suite/src/lib/tool-args.ts +137 -0
  320. package/external/pi-tools-suite/src/lsp/_shared/config.ts +156 -0
  321. package/external/pi-tools-suite/src/lsp/_shared/glob.ts +60 -0
  322. package/external/pi-tools-suite/src/lsp/_shared/output.ts +102 -0
  323. package/external/pi-tools-suite/src/lsp/_shared/paths.ts +138 -0
  324. package/external/pi-tools-suite/src/lsp/_shared/runner.ts +64 -0
  325. package/external/pi-tools-suite/src/lsp/_shared/template.ts +23 -0
  326. package/external/pi-tools-suite/src/lsp/_shared/trust.ts +116 -0
  327. package/external/pi-tools-suite/src/lsp/_shared/types.ts +98 -0
  328. package/external/pi-tools-suite/src/lsp/async.ts +29 -0
  329. package/external/pi-tools-suite/src/lsp/child-process.ts +81 -0
  330. package/external/pi-tools-suite/src/lsp/client.ts +340 -0
  331. package/external/pi-tools-suite/src/lsp/constants.ts +9 -0
  332. package/external/pi-tools-suite/src/lsp/diagnostics-store.ts +64 -0
  333. package/external/pi-tools-suite/src/lsp/documents.ts +24 -0
  334. package/external/pi-tools-suite/src/lsp/index.ts +31 -0
  335. package/external/pi-tools-suite/src/lsp/lsp-utils.ts +37 -0
  336. package/external/pi-tools-suite/src/lsp/manager.ts +190 -0
  337. package/external/pi-tools-suite/src/lsp/mutation-events.ts +78 -0
  338. package/external/pi-tools-suite/src/lsp/renderer.ts +1 -0
  339. package/external/pi-tools-suite/src/lsp/tool-result.ts +6 -0
  340. package/external/pi-tools-suite/src/lsp/tsserver.ts +107 -0
  341. package/external/pi-tools-suite/src/lsp/types.ts +15 -0
  342. package/external/pi-tools-suite/src/model-tools/apply-patch.ts +590 -0
  343. package/external/pi-tools-suite/src/model-tools/index.ts +430 -0
  344. package/external/pi-tools-suite/src/model-tools/path-utils.ts +6 -0
  345. package/external/pi-tools-suite/src/model-tools/tool-args.ts +11 -0
  346. package/external/pi-tools-suite/src/prompt-commands/index.ts +349 -0
  347. package/external/pi-tools-suite/src/repo-discovery/index.ts +384 -0
  348. package/external/pi-tools-suite/src/repo-discovery/project.ts +7 -0
  349. package/external/pi-tools-suite/src/startup-section.ts +13 -0
  350. package/external/pi-tools-suite/src/terminal-bell/index.ts +309 -0
  351. package/external/pi-tools-suite/src/todo/index.ts +201 -0
  352. package/external/pi-tools-suite/src/todo/state/auto-clear.ts +13 -0
  353. package/external/pi-tools-suite/src/todo/state/invariants.ts +21 -0
  354. package/external/pi-tools-suite/src/todo/state/persistence.ts +94 -0
  355. package/external/pi-tools-suite/src/todo/state/replay.ts +38 -0
  356. package/external/pi-tools-suite/src/todo/state/selectors.ts +49 -0
  357. package/external/pi-tools-suite/src/todo/state/state-reducer.ts +362 -0
  358. package/external/pi-tools-suite/src/todo/state/state.ts +18 -0
  359. package/external/pi-tools-suite/src/todo/state/store.ts +52 -0
  360. package/external/pi-tools-suite/src/todo/state/task-graph.ts +57 -0
  361. package/external/pi-tools-suite/src/todo/todo.ts +487 -0
  362. package/external/pi-tools-suite/src/todo/tool/response-envelope.ts +143 -0
  363. package/external/pi-tools-suite/src/todo/tool/types.ts +188 -0
  364. package/external/pi-tools-suite/src/todo/view/format.ts +18 -0
  365. package/external/pi-tools-suite/src/todo/view/labels.ts +13 -0
  366. package/external/pi-tools-suite/src/tool-descriptions.ts +369 -0
  367. package/external/pi-tools-suite/src/usage/index.ts +152 -0
  368. package/external/pi-tools-suite/src/usage/lib/copilot.ts +535 -0
  369. package/external/pi-tools-suite/src/usage/lib/google.ts +478 -0
  370. package/external/pi-tools-suite/src/usage/lib/openai.ts +268 -0
  371. package/external/pi-tools-suite/src/usage/lib/types.ts +114 -0
  372. package/external/pi-tools-suite/src/usage/lib/utils.ts +134 -0
  373. package/external/pi-tools-suite/src/usage/lib/zhipu.ts +228 -0
  374. package/external/pi-tools-suite/src/web-search/index.ts +397 -0
  375. package/package.json +89 -0
  376. package/skills/context7/SKILL.md +69 -0
  377. package/skills/context7/scripts/context7.sh +73 -0
  378. package/skills/handoff/SKILL.md +15 -0
  379. package/skills/pdf/LICENSE.txt +30 -0
  380. package/skills/pdf/SKILL.md +314 -0
  381. package/skills/pdf/forms.md +294 -0
  382. package/skills/pdf/reference.md +612 -0
  383. package/skills/pdf/scripts/check_bounding_boxes.py +65 -0
  384. package/skills/pdf/scripts/check_fillable_fields.py +11 -0
  385. package/skills/pdf/scripts/convert_pdf_to_images.py +33 -0
  386. package/skills/pdf/scripts/create_validation_image.py +37 -0
  387. package/skills/pdf/scripts/extract_form_field_info.py +122 -0
  388. package/skills/pdf/scripts/extract_form_structure.py +115 -0
  389. package/skills/pdf/scripts/fill_fillable_fields.py +98 -0
  390. package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
  391. package/skills/playwright-cli/SKILL.md +388 -0
  392. package/skills/playwright-cli/references/element-attributes.md +23 -0
  393. package/skills/playwright-cli/references/playwright-tests.md +39 -0
  394. package/skills/playwright-cli/references/request-mocking.md +87 -0
  395. package/skills/playwright-cli/references/running-code.md +241 -0
  396. package/skills/playwright-cli/references/session-management.md +225 -0
  397. package/skills/playwright-cli/references/spec-driven-testing.md +305 -0
  398. package/skills/playwright-cli/references/storage-state.md +275 -0
  399. package/skills/playwright-cli/references/test-generation.md +134 -0
  400. package/skills/playwright-cli/references/tracing.md +139 -0
  401. package/skills/playwright-cli/references/video-recording.md +143 -0
  402. package/skills/simplify/SKILL.md +51 -0
  403. package/skills/skill-creator/LICENSE.txt +202 -0
  404. package/skills/skill-creator/SKILL.md +485 -0
  405. package/skills/skill-creator/agents/analyzer.md +274 -0
  406. package/skills/skill-creator/agents/comparator.md +202 -0
  407. package/skills/skill-creator/agents/grader.md +223 -0
  408. package/skills/skill-creator/assets/eval_review.html +146 -0
  409. package/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  410. package/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  411. package/skills/skill-creator/references/schemas.md +430 -0
  412. package/skills/skill-creator/scripts/__init__.py +0 -0
  413. package/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  414. package/skills/skill-creator/scripts/generate_report.py +326 -0
  415. package/skills/skill-creator/scripts/improve_description.py +247 -0
  416. package/skills/skill-creator/scripts/package_skill.py +136 -0
  417. package/skills/skill-creator/scripts/quick_validate.py +103 -0
  418. package/skills/skill-creator/scripts/run_eval.py +310 -0
  419. package/skills/skill-creator/scripts/run_loop.py +328 -0
  420. package/skills/skill-creator/scripts/utils.py +47 -0
@@ -0,0 +1,486 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Types
3
+ // ---------------------------------------------------------------------------
4
+
5
+ import type { DcpNudgeType } from "./pruner-types.js"
6
+
7
+ /**
8
+ * A record of a single tool call, keyed by toolCallId in DcpState.toolCalls.
9
+ */
10
+ export interface ToolRecord {
11
+ /** Matches ToolResultMessage.toolCallId */
12
+ toolCallId: string
13
+ /** Matches ToolResultMessage.toolName */
14
+ toolName: string
15
+ /** The arguments passed to the tool (from the corresponding ToolCall) */
16
+ inputArgs: Record<string, unknown>
17
+ /**
18
+ * Deduplication fingerprint: `toolName::JSON(sortedArgs)`
19
+ * Two calls with the same name + identical args share the same fingerprint.
20
+ */
21
+ inputFingerprint: string
22
+ /** Whether the tool result was an error */
23
+ isError: boolean
24
+ /**
25
+ * Zero-based index of the user turn during which this tool was called.
26
+ * Incremented each time a user message is encountered in the context stream.
27
+ */
28
+ turnIndex: number
29
+ /** message.timestamp from the ToolResultMessage */
30
+ timestamp: number
31
+ /** Rough token estimate: sum of result text content lengths divided by 4 */
32
+ tokenEstimate: number
33
+ /** Completed textual tool output captured for protected-summary preservation. */
34
+ outputText?: string
35
+ /** Optional tool-specific result details captured for protected-summary preservation. */
36
+ outputDetails?: unknown
37
+ }
38
+
39
+ export interface MessageIdMeta {
40
+ /** The actual message.timestamp associated with the visible DCP id. */
41
+ timestamp: number
42
+ /** Stable raw message key when available; falls back to timestamp-derived key. */
43
+ stableId?: string
44
+ /** The message role at the time the id was injected. */
45
+ role: string
46
+ /** Present when this visible message represents an active compression block. */
47
+ blockId?: number
48
+ /** Tool call metadata for tool-result-like messages. */
49
+ toolCallId?: string
50
+ toolName?: string
51
+ /** Plain text extracted from the message when the id was injected. */
52
+ text?: string
53
+ /** Rough token estimate for priority/candidate guidance. */
54
+ tokenEstimate?: number
55
+ /** Optional compression priority marker exposed with the visible message ID. */
56
+ priority?: "low" | "medium" | "high"
57
+ }
58
+
59
+ /**
60
+ * A compression block created by the `compress` tool.
61
+ * Tracks the range of messages that were summarised and where to inject the
62
+ * summary back into the context.
63
+ */
64
+ export interface CompressionBlock {
65
+ /** Auto-incrementing integer ID */
66
+ id: number
67
+ /** Short human-readable topic label */
68
+ topic: string
69
+ /** LLM-generated summary text */
70
+ summary: string
71
+ /** Timestamp of the first message in the compressed range */
72
+ startTimestamp: number
73
+ /** Timestamp of the last message in the compressed range */
74
+ endTimestamp: number
75
+ /** Stable key of the first raw message when known. */
76
+ startMessageId?: string
77
+ /** Stable key of the last raw message when known. */
78
+ endMessageId?: string
79
+ /**
80
+ * Timestamp of the first message *after* the range — the summary is injected
81
+ * immediately before this message. Set to `Infinity` when the range extends
82
+ * to the end of the conversation.
83
+ */
84
+ anchorTimestamp: number
85
+ /** Stable key of the raw message immediately after the range when known. */
86
+ anchorMessageId?: string
87
+ /** Tool call ID of the compress invocation that created this block. */
88
+ createdByToolCallId?: string
89
+ /** Whether this block is still being applied (false = soft-deleted) */
90
+ active: boolean
91
+ /** Token estimate for the summary text itself */
92
+ summaryTokenEstimate: number
93
+ /** Wall-clock time the block was created (Date.now()) */
94
+ createdAt: number
95
+ /** Older compression blocks subsumed by this block during roll-up. */
96
+ coveredBlockIds?: number[]
97
+ /** Whether this block was created from a range or a single raw message. */
98
+ mode?: "range" | "message"
99
+ /** Set when a user explicitly decompressed this block via /dcp decompress. */
100
+ deactivatedByUser?: boolean
101
+ /** Internal reason for automatic soft-deactivation. */
102
+ deactivatedReason?: string
103
+ }
104
+
105
+ export interface DcpNudgeAnchor {
106
+ /** Monotonic local identifier for persisted reminder anchors. */
107
+ id: number
108
+ /** Kind of reminder that should be re-injected at this anchor. */
109
+ type: DcpNudgeType
110
+ /** Timestamp of the message that owns the reminder. */
111
+ anchorTimestamp: number
112
+ /** Stable raw message key for the anchor when available. */
113
+ anchorStableId?: string
114
+ /** Role of the anchored message at creation time. */
115
+ anchorRole: string
116
+ /** User turn index at creation time. */
117
+ turnIndex: number
118
+ /** Approximate context usage that triggered the reminder, as a 0-1 fraction. */
119
+ contextPercent?: number
120
+ /** Wall-clock creation time. */
121
+ createdAt: number
122
+ /** Wall-clock time of the latest re-application/update. */
123
+ updatedAt: number
124
+ }
125
+
126
+ export interface DcpLastNudge {
127
+ type: DcpNudgeType
128
+ anchorId: number
129
+ anchorTimestamp: number
130
+ anchorStableId?: string
131
+ contextPercent?: number
132
+ createdAt: number
133
+ }
134
+
135
+ /**
136
+ * Full runtime state for the DCP extension.
137
+ */
138
+ export interface DcpState {
139
+ // ── Tool tracking ──────────────────────────────────────────────────────────
140
+ /** toolCallId → ToolRecord, populated when a tool_result event fires */
141
+ toolCalls: Map<string, ToolRecord>
142
+ /** Set of toolCallIds whose result messages should be suppressed in context */
143
+ prunedToolIds: Set<string>
144
+ /** toolCallId → reason used for human-readable pruning placeholders/stats. */
145
+ prunedToolReasons: Map<string, string>
146
+
147
+ // ── Compression ────────────────────────────────────────────────────────────
148
+ /** All compression blocks (both active and soft-deleted) */
149
+ compressionBlocks: CompressionBlock[]
150
+ /** Monotonically increasing counter used to assign CompressionBlock.id */
151
+ nextBlockId: number
152
+
153
+ // ── Message ID snapshot ────────────────────────────────────────────────────
154
+ /**
155
+ * Maps the short LLM-visible message IDs (e.g. "m001") to the actual
156
+ * `timestamp` of that message as seen in the last `context` event.
157
+ *
158
+ * The `compress` tool receives ID strings from the LLM; this map lets us
159
+ * translate them back to real timestamps so compression blocks can reference
160
+ * message positions by timestamp (which is stable across pruning passes).
161
+ */
162
+ messageIdSnapshot: Map<string, number>
163
+ /** Extra metadata for the visible DCP message IDs in messageIdSnapshot. */
164
+ messageMetaSnapshot: Map<string, MessageIdMeta>
165
+
166
+ // ── Turn tracking ──────────────────────────────────────────────────────────
167
+ /**
168
+ * Zero-based index of the current user turn.
169
+ * Incremented each time a user message is encountered while processing the
170
+ * context array in the `context` event handler.
171
+ */
172
+ currentTurn: number
173
+
174
+ // ── Statistics ─────────────────────────────────────────────────────────────
175
+ /** Running total of tokens estimated to have been saved by pruning/compression */
176
+ tokensSaved: number
177
+ /** Number of discrete pruning operations performed */
178
+ totalPruneCount: number
179
+ /** Compression block IDs already counted in tokensSaved/totalPruneCount. */
180
+ accountedCompressionBlockIds: Set<number>
181
+ /** compressionBlockId → raw active-token savings estimate for that block. */
182
+ compressionTokenSavings: Map<number, number>
183
+ /** Tool result IDs already counted in tokensSaved/totalPruneCount. */
184
+ accountedPrunedToolIds: Set<string>
185
+ // ── Mode ───────────────────────────────────────────────────────────────────
186
+ /**
187
+ * When true, the extension will not autonomously emit compress nudges.
188
+ * Automatic deduplication/error-purge strategies may still run depending on
189
+ * the `manualMode.automaticStrategies` config flag.
190
+ */
191
+ manualMode: boolean
192
+
193
+ // ── Nudge state ────────────────────────────────────────────────────────────
194
+ /**
195
+ * How many `context` events have fired since the last compress nudge was
196
+ * emitted. Reset to 0 after each nudge.
197
+ */
198
+ nudgeCounter: number
199
+ /**
200
+ * The value of `currentTurn` at the time the last nudge was emitted.
201
+ * Kept as diagnostic/session state only. Nudges are intentionally not
202
+ * throttled to once per user turn: long autonomous work loops can consume a
203
+ * lot of context before the next user message, so nudgeFrequency must be able
204
+ * to emit repeated reminders inside the same turn.
205
+ */
206
+ lastNudgeTurn: number
207
+ /** Persisted anchors where active reminders are injected until compression happens. */
208
+ nudgeAnchors: DcpNudgeAnchor[]
209
+ /** Next monotonic anchor ID. */
210
+ nextNudgeAnchorId: number
211
+ /** Diagnostic/telemetry snapshot for the latest emitted reminder. */
212
+ lastNudge?: DcpLastNudge
213
+ }
214
+
215
+ // ---------------------------------------------------------------------------
216
+ // Factory functions
217
+ // ---------------------------------------------------------------------------
218
+
219
+ /** Create a fresh, zeroed DcpState instance. */
220
+ export function createState(): DcpState {
221
+ return {
222
+ toolCalls: new Map(),
223
+ prunedToolIds: new Set(),
224
+ prunedToolReasons: new Map(),
225
+ compressionBlocks: [],
226
+ nextBlockId: 1,
227
+ messageIdSnapshot: new Map(),
228
+ messageMetaSnapshot: new Map(),
229
+ currentTurn: 0,
230
+ tokensSaved: 0,
231
+ totalPruneCount: 0,
232
+ accountedCompressionBlockIds: new Set(),
233
+ compressionTokenSavings: new Map(),
234
+ accountedPrunedToolIds: new Set(),
235
+ manualMode: false,
236
+ nudgeCounter: 0,
237
+ lastNudgeTurn: -1,
238
+ nudgeAnchors: [],
239
+ nextNudgeAnchorId: 1,
240
+ lastNudge: undefined,
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Reset `state` back to its initial values **in-place**.
246
+ * Preserves the object reference so other modules holding a reference see the
247
+ * reset immediately.
248
+ */
249
+ export function resetState(state: DcpState): void {
250
+ state.toolCalls.clear()
251
+ state.prunedToolIds.clear()
252
+ state.prunedToolReasons.clear()
253
+ state.compressionBlocks = []
254
+ state.nextBlockId = 1
255
+ state.messageIdSnapshot.clear()
256
+ state.messageMetaSnapshot.clear()
257
+ state.currentTurn = 0
258
+ state.tokensSaved = 0
259
+ state.totalPruneCount = 0
260
+ state.accountedCompressionBlockIds.clear()
261
+ state.compressionTokenSavings.clear()
262
+ state.accountedPrunedToolIds.clear()
263
+ state.manualMode = false
264
+ state.nudgeCounter = 0
265
+ state.lastNudgeTurn = -1
266
+ state.nudgeAnchors = []
267
+ state.nextNudgeAnchorId = 1
268
+ state.lastNudge = undefined
269
+ }
270
+
271
+ export interface SerializedDcpState {
272
+ compressionBlocks: CompressionBlock[]
273
+ nextBlockId: number
274
+ prunedToolIds: string[]
275
+ prunedToolReasons: Array<[string, string]>
276
+ toolCalls: ToolRecord[]
277
+ tokensSaved: number
278
+ totalPruneCount: number
279
+ accountedCompressionBlockIds: number[]
280
+ compressionTokenSavings: Array<[number, number]>
281
+ accountedPrunedToolIds: string[]
282
+ manualMode: boolean
283
+ nudgeAnchors?: DcpNudgeAnchor[]
284
+ nextNudgeAnchorId?: number
285
+ lastNudge?: DcpLastNudge
286
+ }
287
+
288
+ function isToolRecord(value: unknown): value is ToolRecord {
289
+ if (!value || typeof value !== "object") return false
290
+ const record = value as Partial<ToolRecord>
291
+ return (
292
+ typeof record.toolCallId === "string" &&
293
+ typeof record.toolName === "string" &&
294
+ typeof record.inputFingerprint === "string"
295
+ )
296
+ }
297
+
298
+ function isNudgeAnchor(value: unknown): value is DcpNudgeAnchor {
299
+ if (!value || typeof value !== "object") return false
300
+ const anchor = value as Partial<DcpNudgeAnchor>
301
+ return (
302
+ typeof anchor.id === "number" &&
303
+ typeof anchor.type === "string" &&
304
+ Number.isFinite(anchor.anchorTimestamp) &&
305
+ typeof anchor.anchorRole === "string" &&
306
+ typeof anchor.turnIndex === "number" &&
307
+ typeof anchor.createdAt === "number" &&
308
+ typeof anchor.updatedAt === "number"
309
+ )
310
+ }
311
+
312
+ function isLastNudge(value: unknown): value is DcpLastNudge {
313
+ if (!value || typeof value !== "object") return false
314
+ const nudge = value as Partial<DcpLastNudge>
315
+ return (
316
+ typeof nudge.type === "string" &&
317
+ typeof nudge.anchorId === "number" &&
318
+ Number.isFinite(nudge.anchorTimestamp) &&
319
+ typeof nudge.createdAt === "number"
320
+ )
321
+ }
322
+
323
+ /** Serialize runtime state into a JSON-safe object for pi.appendEntry(). */
324
+ export function serializeState(state: DcpState): SerializedDcpState {
325
+ return {
326
+ compressionBlocks: state.compressionBlocks,
327
+ nextBlockId: state.nextBlockId,
328
+ prunedToolIds: Array.from(state.prunedToolIds),
329
+ prunedToolReasons: Array.from(state.prunedToolReasons.entries()),
330
+ toolCalls: Array.from(state.toolCalls.values()),
331
+ tokensSaved: state.tokensSaved,
332
+ totalPruneCount: state.totalPruneCount,
333
+ accountedCompressionBlockIds: Array.from(state.accountedCompressionBlockIds),
334
+ compressionTokenSavings: Array.from(state.compressionTokenSavings.entries()),
335
+ accountedPrunedToolIds: Array.from(state.accountedPrunedToolIds),
336
+ manualMode: state.manualMode,
337
+ nudgeAnchors: state.nudgeAnchors,
338
+ nextNudgeAnchorId: state.nextNudgeAnchorId,
339
+ lastNudge: state.lastNudge,
340
+ }
341
+ }
342
+
343
+ /**
344
+ * Restore persisted DCP state into an already-reset state object.
345
+ * Handles older persisted payloads that did not include the newer accounting
346
+ * and tool-call fingerprint fields.
347
+ */
348
+ export function restoreState(state: DcpState, data: unknown): void {
349
+ if (!data || typeof data !== "object") return
350
+ const saved = data as Partial<SerializedDcpState>
351
+
352
+ if (Array.isArray(saved.compressionBlocks)) {
353
+ const validBlocks = saved.compressionBlocks
354
+ .filter((b: any) => Number.isFinite(b?.startTimestamp) && Number.isFinite(b?.endTimestamp))
355
+ .map((b: any) => ({
356
+ ...b,
357
+ anchorTimestamp: Number.isFinite(b.anchorTimestamp)
358
+ ? b.anchorTimestamp
359
+ : b.endTimestamp + 1,
360
+ })) as CompressionBlock[]
361
+ state.compressionBlocks = validBlocks
362
+ state.nextBlockId =
363
+ typeof saved.nextBlockId === "number"
364
+ ? saved.nextBlockId
365
+ : validBlocks.length > 0
366
+ ? Math.max(0, ...validBlocks.map((b) => b.id)) + 1
367
+ : 1
368
+ }
369
+
370
+ if (Array.isArray(saved.prunedToolIds)) {
371
+ state.prunedToolIds = new Set(saved.prunedToolIds.filter((id): id is string => typeof id === "string"))
372
+ }
373
+
374
+ if (Array.isArray(saved.prunedToolReasons)) {
375
+ state.prunedToolReasons = new Map(
376
+ saved.prunedToolReasons.filter(
377
+ (entry): entry is [string, string] =>
378
+ Array.isArray(entry) && typeof entry[0] === "string" && typeof entry[1] === "string",
379
+ ),
380
+ )
381
+ }
382
+
383
+ if (Array.isArray(saved.toolCalls)) {
384
+ state.toolCalls = new Map(
385
+ saved.toolCalls
386
+ .filter(isToolRecord)
387
+ .map((record) => [record.toolCallId, record] as const),
388
+ )
389
+ }
390
+
391
+ if (typeof saved.tokensSaved === "number") state.tokensSaved = saved.tokensSaved
392
+ if (typeof saved.totalPruneCount === "number") state.totalPruneCount = saved.totalPruneCount
393
+
394
+ if (Array.isArray(saved.accountedCompressionBlockIds)) {
395
+ state.accountedCompressionBlockIds = new Set(
396
+ saved.accountedCompressionBlockIds.filter((id): id is number => typeof id === "number"),
397
+ )
398
+ } else {
399
+ // Old payloads already persisted aggregate stats but not per-block accounting.
400
+ // Treat existing active blocks as accounted to avoid inflated stats after reload.
401
+ state.accountedCompressionBlockIds = new Set(state.compressionBlocks.map((b) => b.id))
402
+ }
403
+
404
+ if (Array.isArray(saved.compressionTokenSavings)) {
405
+ state.compressionTokenSavings = new Map(
406
+ saved.compressionTokenSavings.filter(
407
+ (entry): entry is [number, number] =>
408
+ Array.isArray(entry) && typeof entry[0] === "number" && typeof entry[1] === "number",
409
+ ),
410
+ )
411
+ }
412
+
413
+ if (Array.isArray(saved.accountedPrunedToolIds)) {
414
+ state.accountedPrunedToolIds = new Set(
415
+ saved.accountedPrunedToolIds.filter((id): id is string => typeof id === "string"),
416
+ )
417
+ } else {
418
+ // Same migration safety for pre-accounting payloads.
419
+ state.accountedPrunedToolIds = new Set(state.prunedToolIds)
420
+ }
421
+
422
+ if (typeof saved.manualMode === "boolean") {
423
+ state.manualMode = saved.manualMode
424
+ }
425
+
426
+ if (Array.isArray(saved.nudgeAnchors)) {
427
+ const seen = new Set<string>()
428
+ state.nudgeAnchors = saved.nudgeAnchors
429
+ .filter(isNudgeAnchor)
430
+ .filter((anchor) => {
431
+ const key = `${anchor.anchorStableId ?? ""}|${anchor.anchorTimestamp}`
432
+ if (seen.has(key)) return false
433
+ seen.add(key)
434
+ return true
435
+ })
436
+ }
437
+
438
+ if (typeof saved.nextNudgeAnchorId === "number" && saved.nextNudgeAnchorId > 0) {
439
+ state.nextNudgeAnchorId = Math.floor(saved.nextNudgeAnchorId)
440
+ } else if (state.nudgeAnchors.length > 0) {
441
+ state.nextNudgeAnchorId = Math.max(...state.nudgeAnchors.map((anchor) => anchor.id)) + 1
442
+ }
443
+
444
+ if (isLastNudge(saved.lastNudge)) {
445
+ state.lastNudge = saved.lastNudge
446
+ }
447
+ }
448
+
449
+ // ---------------------------------------------------------------------------
450
+ // Fingerprinting
451
+ // ---------------------------------------------------------------------------
452
+
453
+ /**
454
+ * Recursively sort the keys of a plain object so that two argument objects
455
+ * with the same entries in different key-insertion order produce the same JSON.
456
+ */
457
+ function sortObjectKeys(value: unknown): unknown {
458
+ if (Array.isArray(value)) {
459
+ return value.map(sortObjectKeys)
460
+ }
461
+ if (value !== null && typeof value === "object") {
462
+ const obj = value as Record<string, unknown>
463
+ const sorted: Record<string, unknown> = {}
464
+ for (const key of Object.keys(obj).sort()) {
465
+ sorted[key] = sortObjectKeys(obj[key])
466
+ }
467
+ return sorted
468
+ }
469
+ return value
470
+ }
471
+
472
+ /**
473
+ * Create a stable deduplication fingerprint for a tool call.
474
+ *
475
+ * Two calls with the same `toolName` and semantically identical `args`
476
+ * (regardless of key ordering) will produce the same fingerprint.
477
+ *
478
+ * Format: `<toolName>::<JSON of recursively key-sorted args>`
479
+ */
480
+ export function createInputFingerprint(
481
+ toolName: string,
482
+ args: Record<string, unknown>,
483
+ ): string {
484
+ const sorted = sortObjectKeys(args)
485
+ return `${toolName}::${JSON.stringify(sorted)}`
486
+ }