@code-yeongyu/senpi 2026.6.15 → 2026.6.16

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 (299) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/core/compaction/compaction.d.ts.map +1 -1
  3. package/dist/core/compaction/compaction.js +5 -0
  4. package/dist/core/compaction/compaction.js.map +1 -1
  5. package/dist/core/extensions/builtin/bash-timeout/index.d.ts.map +1 -1
  6. package/dist/core/extensions/builtin/bash-timeout/index.js +3 -1
  7. package/dist/core/extensions/builtin/bash-timeout/index.js.map +1 -1
  8. package/dist/core/extensions/builtin/bash-timeout/timeout.js +2 -2
  9. package/dist/core/extensions/builtin/bash-timeout/timeout.js.map +1 -1
  10. package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
  11. package/dist/core/extensions/builtin/compaction/speculative.js +24 -12
  12. package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
  13. package/dist/core/extensions/builtin/goal/command.d.ts +14 -0
  14. package/dist/core/extensions/builtin/goal/command.d.ts.map +1 -0
  15. package/dist/core/extensions/builtin/goal/command.js +16 -0
  16. package/dist/core/extensions/builtin/goal/command.js.map +1 -0
  17. package/dist/core/extensions/builtin/goal/continuation.d.ts +4 -0
  18. package/dist/core/extensions/builtin/goal/continuation.d.ts.map +1 -0
  19. package/dist/core/extensions/builtin/goal/continuation.js +7 -0
  20. package/dist/core/extensions/builtin/goal/continuation.js.map +1 -0
  21. package/dist/core/extensions/builtin/goal/errors.d.ts +13 -0
  22. package/dist/core/extensions/builtin/goal/errors.d.ts.map +1 -0
  23. package/dist/core/extensions/builtin/goal/errors.js +25 -0
  24. package/dist/core/extensions/builtin/goal/errors.js.map +1 -0
  25. package/dist/core/extensions/builtin/goal/format.d.ts +8 -0
  26. package/dist/core/extensions/builtin/goal/format.d.ts.map +1 -0
  27. package/dist/core/extensions/builtin/goal/format.js +71 -0
  28. package/dist/core/extensions/builtin/goal/format.js.map +1 -0
  29. package/dist/core/extensions/builtin/goal/index.d.ts +3 -0
  30. package/dist/core/extensions/builtin/goal/index.d.ts.map +1 -0
  31. package/dist/core/extensions/builtin/goal/index.js +322 -0
  32. package/dist/core/extensions/builtin/goal/index.js.map +1 -0
  33. package/dist/core/extensions/builtin/goal/prompt.d.ts +3 -0
  34. package/dist/core/extensions/builtin/goal/prompt.d.ts.map +1 -0
  35. package/dist/core/extensions/builtin/goal/prompt.js +34 -0
  36. package/dist/core/extensions/builtin/goal/prompt.js.map +1 -0
  37. package/dist/core/extensions/builtin/goal/store.d.ts +9 -0
  38. package/dist/core/extensions/builtin/goal/store.d.ts.map +1 -0
  39. package/dist/core/extensions/builtin/goal/store.js +173 -0
  40. package/dist/core/extensions/builtin/goal/store.js.map +1 -0
  41. package/dist/core/extensions/builtin/goal/types.d.ts +50 -0
  42. package/dist/core/extensions/builtin/goal/types.d.ts.map +1 -0
  43. package/dist/core/extensions/builtin/goal/types.js +6 -0
  44. package/dist/core/extensions/builtin/goal/types.js.map +1 -0
  45. package/dist/core/extensions/builtin/goal/ui.d.ts +6 -0
  46. package/dist/core/extensions/builtin/goal/ui.d.ts.map +1 -0
  47. package/dist/core/extensions/builtin/goal/ui.js +20 -0
  48. package/dist/core/extensions/builtin/goal/ui.js.map +1 -0
  49. package/dist/core/extensions/builtin/goal/validation.d.ts +3 -0
  50. package/dist/core/extensions/builtin/goal/validation.d.ts.map +1 -0
  51. package/dist/core/extensions/builtin/goal/validation.js +13 -0
  52. package/dist/core/extensions/builtin/goal/validation.js.map +1 -0
  53. package/dist/core/extensions/builtin/index.d.ts.map +1 -1
  54. package/dist/core/extensions/builtin/index.js +10 -2
  55. package/dist/core/extensions/builtin/index.js.map +1 -1
  56. package/dist/core/extensions/builtin/nested-agents-md/core/containment.d.ts +10 -0
  57. package/dist/core/extensions/builtin/nested-agents-md/core/containment.d.ts.map +1 -0
  58. package/dist/core/extensions/builtin/nested-agents-md/core/containment.js +23 -0
  59. package/dist/core/extensions/builtin/nested-agents-md/core/containment.js.map +1 -0
  60. package/dist/core/extensions/builtin/nested-agents-md/core/errors.d.ts +5 -0
  61. package/dist/core/extensions/builtin/nested-agents-md/core/errors.d.ts.map +1 -0
  62. package/dist/core/extensions/builtin/nested-agents-md/core/errors.js +9 -0
  63. package/dist/core/extensions/builtin/nested-agents-md/core/errors.js.map +1 -0
  64. package/dist/core/extensions/builtin/nested-agents-md/core/find-agents-md-up.d.ts +7 -0
  65. package/dist/core/extensions/builtin/nested-agents-md/core/find-agents-md-up.d.ts.map +1 -0
  66. package/dist/core/extensions/builtin/nested-agents-md/core/find-agents-md-up.js +44 -0
  67. package/dist/core/extensions/builtin/nested-agents-md/core/find-agents-md-up.js.map +1 -0
  68. package/dist/core/extensions/builtin/nested-agents-md/core/format.d.ts +7 -0
  69. package/dist/core/extensions/builtin/nested-agents-md/core/format.d.ts.map +1 -0
  70. package/dist/core/extensions/builtin/nested-agents-md/core/format.js +7 -0
  71. package/dist/core/extensions/builtin/nested-agents-md/core/format.js.map +1 -0
  72. package/dist/core/extensions/builtin/nested-agents-md/core/inject-directory-context.d.ts +11 -0
  73. package/dist/core/extensions/builtin/nested-agents-md/core/inject-directory-context.d.ts.map +1 -0
  74. package/dist/core/extensions/builtin/nested-agents-md/core/inject-directory-context.js +77 -0
  75. package/dist/core/extensions/builtin/nested-agents-md/core/inject-directory-context.js.map +1 -0
  76. package/dist/core/extensions/builtin/nested-agents-md/core/injection-cache.d.ts +11 -0
  77. package/dist/core/extensions/builtin/nested-agents-md/core/injection-cache.d.ts.map +1 -0
  78. package/dist/core/extensions/builtin/nested-agents-md/core/injection-cache.js +33 -0
  79. package/dist/core/extensions/builtin/nested-agents-md/core/injection-cache.js.map +1 -0
  80. package/dist/core/extensions/builtin/nested-agents-md/core/session-key.d.ts +8 -0
  81. package/dist/core/extensions/builtin/nested-agents-md/core/session-key.d.ts.map +1 -0
  82. package/dist/core/extensions/builtin/nested-agents-md/core/session-key.js +8 -0
  83. package/dist/core/extensions/builtin/nested-agents-md/core/session-key.js.map +1 -0
  84. package/dist/core/extensions/builtin/nested-agents-md/core/truncate.d.ts +8 -0
  85. package/dist/core/extensions/builtin/nested-agents-md/core/truncate.d.ts.map +1 -0
  86. package/dist/core/extensions/builtin/nested-agents-md/core/truncate.js +26 -0
  87. package/dist/core/extensions/builtin/nested-agents-md/core/truncate.js.map +1 -0
  88. package/dist/core/extensions/builtin/nested-agents-md/core/types.d.ts +25 -0
  89. package/dist/core/extensions/builtin/nested-agents-md/core/types.d.ts.map +1 -0
  90. package/dist/core/extensions/builtin/nested-agents-md/core/types.js +4 -0
  91. package/dist/core/extensions/builtin/nested-agents-md/core/types.js.map +1 -0
  92. package/dist/core/extensions/builtin/nested-agents-md/index.d.ts +3 -0
  93. package/dist/core/extensions/builtin/nested-agents-md/index.d.ts.map +1 -0
  94. package/dist/core/extensions/builtin/nested-agents-md/index.js +102 -0
  95. package/dist/core/extensions/builtin/nested-agents-md/index.js.map +1 -0
  96. package/dist/core/extensions/builtin/nested-agents-md/ui/reporter.d.ts +32 -0
  97. package/dist/core/extensions/builtin/nested-agents-md/ui/reporter.d.ts.map +1 -0
  98. package/dist/core/extensions/builtin/nested-agents-md/ui/reporter.js +66 -0
  99. package/dist/core/extensions/builtin/nested-agents-md/ui/reporter.js.map +1 -0
  100. package/dist/core/extensions/builtin/prompt-preset/presets.js +2 -2
  101. package/dist/core/extensions/builtin/prompt-preset/presets.js.map +1 -1
  102. package/dist/core/extensions/builtin/rules/commands.d.ts +4 -0
  103. package/dist/core/extensions/builtin/rules/commands.d.ts.map +1 -0
  104. package/dist/core/extensions/builtin/rules/commands.js +87 -0
  105. package/dist/core/extensions/builtin/rules/commands.js.map +1 -0
  106. package/dist/core/extensions/builtin/rules/index.d.ts +3 -0
  107. package/dist/core/extensions/builtin/rules/index.d.ts.map +1 -0
  108. package/dist/core/extensions/builtin/rules/index.js +127 -0
  109. package/dist/core/extensions/builtin/rules/index.js.map +1 -0
  110. package/dist/core/extensions/builtin/rules/rules/cache.d.ts +10 -0
  111. package/dist/core/extensions/builtin/rules/rules/cache.d.ts.map +1 -0
  112. package/dist/core/extensions/builtin/rules/rules/cache.js +51 -0
  113. package/dist/core/extensions/builtin/rules/rules/cache.js.map +1 -0
  114. package/dist/core/extensions/builtin/rules/rules/constants.d.ts +57 -0
  115. package/dist/core/extensions/builtin/rules/rules/constants.d.ts.map +1 -0
  116. package/dist/core/extensions/builtin/rules/rules/constants.js +96 -0
  117. package/dist/core/extensions/builtin/rules/rules/constants.js.map +1 -0
  118. package/dist/core/extensions/builtin/rules/rules/engine.d.ts +67 -0
  119. package/dist/core/extensions/builtin/rules/rules/engine.d.ts.map +1 -0
  120. package/dist/core/extensions/builtin/rules/rules/engine.js +443 -0
  121. package/dist/core/extensions/builtin/rules/rules/engine.js.map +1 -0
  122. package/dist/core/extensions/builtin/rules/rules/errors.d.ts +7 -0
  123. package/dist/core/extensions/builtin/rules/rules/errors.d.ts.map +1 -0
  124. package/dist/core/extensions/builtin/rules/rules/errors.js +13 -0
  125. package/dist/core/extensions/builtin/rules/rules/errors.js.map +1 -0
  126. package/dist/core/extensions/builtin/rules/rules/finder.d.ts +27 -0
  127. package/dist/core/extensions/builtin/rules/rules/finder.d.ts.map +1 -0
  128. package/dist/core/extensions/builtin/rules/rules/finder.js +241 -0
  129. package/dist/core/extensions/builtin/rules/rules/finder.js.map +1 -0
  130. package/dist/core/extensions/builtin/rules/rules/formatter.d.ts +8 -0
  131. package/dist/core/extensions/builtin/rules/rules/formatter.d.ts.map +1 -0
  132. package/dist/core/extensions/builtin/rules/rules/formatter.js +44 -0
  133. package/dist/core/extensions/builtin/rules/rules/formatter.js.map +1 -0
  134. package/dist/core/extensions/builtin/rules/rules/matcher.d.ts +25 -0
  135. package/dist/core/extensions/builtin/rules/rules/matcher.d.ts.map +1 -0
  136. package/dist/core/extensions/builtin/rules/rules/matcher.js +107 -0
  137. package/dist/core/extensions/builtin/rules/rules/matcher.js.map +1 -0
  138. package/dist/core/extensions/builtin/rules/rules/ordering.d.ts +4 -0
  139. package/dist/core/extensions/builtin/rules/rules/ordering.d.ts.map +1 -0
  140. package/dist/core/extensions/builtin/rules/rules/ordering.js +28 -0
  141. package/dist/core/extensions/builtin/rules/rules/ordering.js.map +1 -0
  142. package/dist/core/extensions/builtin/rules/rules/parser.d.ts +4 -0
  143. package/dist/core/extensions/builtin/rules/rules/parser.d.ts.map +1 -0
  144. package/dist/core/extensions/builtin/rules/rules/parser.js +290 -0
  145. package/dist/core/extensions/builtin/rules/rules/parser.js.map +1 -0
  146. package/dist/core/extensions/builtin/rules/rules/project-root.d.ts +2 -0
  147. package/dist/core/extensions/builtin/rules/rules/project-root.d.ts.map +1 -0
  148. package/dist/core/extensions/builtin/rules/rules/project-root.js +24 -0
  149. package/dist/core/extensions/builtin/rules/rules/project-root.js.map +1 -0
  150. package/dist/core/extensions/builtin/rules/rules/scanner.d.ts +14 -0
  151. package/dist/core/extensions/builtin/rules/rules/scanner.d.ts.map +1 -0
  152. package/dist/core/extensions/builtin/rules/rules/scanner.js +96 -0
  153. package/dist/core/extensions/builtin/rules/rules/scanner.js.map +1 -0
  154. package/dist/core/extensions/builtin/rules/rules/tool-paths.d.ts +4 -0
  155. package/dist/core/extensions/builtin/rules/rules/tool-paths.d.ts.map +1 -0
  156. package/dist/core/extensions/builtin/rules/rules/tool-paths.js +37 -0
  157. package/dist/core/extensions/builtin/rules/rules/tool-paths.js.map +1 -0
  158. package/dist/core/extensions/builtin/rules/rules/truncator.d.ts +18 -0
  159. package/dist/core/extensions/builtin/rules/rules/truncator.d.ts.map +1 -0
  160. package/dist/core/extensions/builtin/rules/rules/truncator.js +46 -0
  161. package/dist/core/extensions/builtin/rules/rules/truncator.js.map +1 -0
  162. package/dist/core/extensions/builtin/rules/rules/types.d.ts +124 -0
  163. package/dist/core/extensions/builtin/rules/rules/types.d.ts.map +1 -0
  164. package/dist/core/extensions/builtin/rules/rules/types.js +9 -0
  165. package/dist/core/extensions/builtin/rules/rules/types.js.map +1 -0
  166. package/dist/core/extensions/builtin/rules/ui/dynamic-border.d.ts +8 -0
  167. package/dist/core/extensions/builtin/rules/ui/dynamic-border.d.ts.map +1 -0
  168. package/dist/core/extensions/builtin/rules/ui/dynamic-border.js +10 -0
  169. package/dist/core/extensions/builtin/rules/ui/dynamic-border.js.map +1 -0
  170. package/dist/core/extensions/builtin/rules/ui/rules-banner.d.ts +22 -0
  171. package/dist/core/extensions/builtin/rules/ui/rules-banner.d.ts.map +1 -0
  172. package/dist/core/extensions/builtin/rules/ui/rules-banner.js +51 -0
  173. package/dist/core/extensions/builtin/rules/ui/rules-banner.js.map +1 -0
  174. package/dist/core/extensions/builtin/webfetch/index.d.ts +10 -0
  175. package/dist/core/extensions/builtin/webfetch/index.d.ts.map +1 -0
  176. package/dist/core/extensions/builtin/webfetch/index.js +53 -0
  177. package/dist/core/extensions/builtin/webfetch/index.js.map +1 -0
  178. package/dist/core/extensions/builtin/webfetch/webfetch/content.d.ts +4 -0
  179. package/dist/core/extensions/builtin/webfetch/webfetch/content.d.ts.map +1 -0
  180. package/dist/core/extensions/builtin/webfetch/webfetch/content.js +60 -0
  181. package/dist/core/extensions/builtin/webfetch/webfetch/content.js.map +1 -0
  182. package/dist/core/extensions/builtin/webfetch/webfetch/errors.d.ts +13 -0
  183. package/dist/core/extensions/builtin/webfetch/webfetch/errors.d.ts.map +1 -0
  184. package/dist/core/extensions/builtin/webfetch/webfetch/errors.js +25 -0
  185. package/dist/core/extensions/builtin/webfetch/webfetch/errors.js.map +1 -0
  186. package/dist/core/extensions/builtin/webfetch/webfetch/fetcher.d.ts +24 -0
  187. package/dist/core/extensions/builtin/webfetch/webfetch/fetcher.d.ts.map +1 -0
  188. package/dist/core/extensions/builtin/webfetch/webfetch/fetcher.js +148 -0
  189. package/dist/core/extensions/builtin/webfetch/webfetch/fetcher.js.map +1 -0
  190. package/dist/core/extensions/builtin/webfetch/webfetch/renderers.d.ts +15 -0
  191. package/dist/core/extensions/builtin/webfetch/webfetch/renderers.d.ts.map +1 -0
  192. package/dist/core/extensions/builtin/webfetch/webfetch/renderers.js +110 -0
  193. package/dist/core/extensions/builtin/webfetch/webfetch/renderers.js.map +1 -0
  194. package/dist/core/extensions/builtin/webfetch/webfetch/tool.d.ts +28 -0
  195. package/dist/core/extensions/builtin/webfetch/webfetch/tool.d.ts.map +1 -0
  196. package/dist/core/extensions/builtin/webfetch/webfetch/tool.js +79 -0
  197. package/dist/core/extensions/builtin/webfetch/webfetch/tool.js.map +1 -0
  198. package/dist/core/extensions/builtin/websearch/index.d.ts +3 -0
  199. package/dist/core/extensions/builtin/websearch/index.d.ts.map +1 -0
  200. package/dist/core/extensions/builtin/websearch/index.js +76 -0
  201. package/dist/core/extensions/builtin/websearch/index.js.map +1 -0
  202. package/dist/core/extensions/builtin/websearch/websearch/config.d.ts +12 -0
  203. package/dist/core/extensions/builtin/websearch/websearch/config.d.ts.map +1 -0
  204. package/dist/core/extensions/builtin/websearch/websearch/config.js +245 -0
  205. package/dist/core/extensions/builtin/websearch/websearch/config.js.map +1 -0
  206. package/dist/core/extensions/builtin/websearch/websearch/native.d.ts +19 -0
  207. package/dist/core/extensions/builtin/websearch/websearch/native.d.ts.map +1 -0
  208. package/dist/core/extensions/builtin/websearch/websearch/native.js +65 -0
  209. package/dist/core/extensions/builtin/websearch/websearch/native.js.map +1 -0
  210. package/dist/core/extensions/builtin/websearch/websearch/provider-endpoints.d.ts +5 -0
  211. package/dist/core/extensions/builtin/websearch/websearch/provider-endpoints.d.ts.map +1 -0
  212. package/dist/core/extensions/builtin/websearch/websearch/provider-endpoints.js +58 -0
  213. package/dist/core/extensions/builtin/websearch/websearch/provider-endpoints.js.map +1 -0
  214. package/dist/core/extensions/builtin/websearch/websearch/providers.d.ts +4 -0
  215. package/dist/core/extensions/builtin/websearch/websearch/providers.d.ts.map +1 -0
  216. package/dist/core/extensions/builtin/websearch/websearch/providers.js +464 -0
  217. package/dist/core/extensions/builtin/websearch/websearch/providers.js.map +1 -0
  218. package/dist/core/extensions/builtin/websearch/websearch/renderers.d.ts +26 -0
  219. package/dist/core/extensions/builtin/websearch/websearch/renderers.d.ts.map +1 -0
  220. package/dist/core/extensions/builtin/websearch/websearch/renderers.js +66 -0
  221. package/dist/core/extensions/builtin/websearch/websearch/renderers.js.map +1 -0
  222. package/dist/core/extensions/builtin/websearch/websearch/search.d.ts +9 -0
  223. package/dist/core/extensions/builtin/websearch/websearch/search.d.ts.map +1 -0
  224. package/dist/core/extensions/builtin/websearch/websearch/search.js +261 -0
  225. package/dist/core/extensions/builtin/websearch/websearch/search.js.map +1 -0
  226. package/dist/core/extensions/builtin/websearch/websearch/tool.d.ts +18 -0
  227. package/dist/core/extensions/builtin/websearch/websearch/tool.d.ts.map +1 -0
  228. package/dist/core/extensions/builtin/websearch/websearch/tool.js +88 -0
  229. package/dist/core/extensions/builtin/websearch/websearch/tool.js.map +1 -0
  230. package/dist/core/extensions/builtin/websearch/websearch/types.d.ts +115 -0
  231. package/dist/core/extensions/builtin/websearch/websearch/types.d.ts.map +1 -0
  232. package/dist/core/extensions/builtin/websearch/websearch/types.js +2 -0
  233. package/dist/core/extensions/builtin/websearch/websearch/types.js.map +1 -0
  234. package/dist/core/extensions/runner.d.ts.map +1 -1
  235. package/dist/core/extensions/runner.js +8 -1
  236. package/dist/core/extensions/runner.js.map +1 -1
  237. package/dist/core/tools/bash.d.ts.map +1 -1
  238. package/dist/core/tools/bash.js +4 -0
  239. package/dist/core/tools/bash.js.map +1 -1
  240. package/dist/modes/interactive/components/diff.d.ts +14 -0
  241. package/dist/modes/interactive/components/diff.d.ts.map +1 -1
  242. package/dist/modes/interactive/components/diff.js +65 -1
  243. package/dist/modes/interactive/components/diff.js.map +1 -1
  244. package/dist/modes/interactive/components/session-selector.d.ts +0 -1
  245. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  246. package/dist/modes/interactive/components/session-selector.js +0 -4
  247. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  248. package/dist/utils/child-process.d.ts +7 -4
  249. package/dist/utils/child-process.d.ts.map +1 -1
  250. package/dist/utils/child-process.js +23 -5
  251. package/dist/utils/child-process.js.map +1 -1
  252. package/docs/containerization.md +36 -36
  253. package/docs/index.md +1 -1
  254. package/docs/security.md +13 -13
  255. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  256. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  257. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  258. package/examples/extensions/gondolin/package-lock.json +2 -2
  259. package/examples/extensions/gondolin/package.json +1 -1
  260. package/examples/extensions/sandbox/package-lock.json +2 -2
  261. package/examples/extensions/sandbox/package.json +1 -1
  262. package/examples/extensions/with-deps/package-lock.json +2 -2
  263. package/examples/extensions/with-deps/package.json +1 -1
  264. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.d.ts.map +1 -1
  265. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js +5 -0
  266. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js.map +1 -1
  267. package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
  268. package/node_modules/@earendil-works/pi-ai/dist/models.d.ts.map +1 -1
  269. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +88 -74
  270. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
  271. package/node_modules/@earendil-works/pi-ai/dist/models.generated.js +124 -132
  272. package/node_modules/@earendil-works/pi-ai/dist/models.generated.js.map +1 -1
  273. package/node_modules/@earendil-works/pi-ai/dist/models.js +4 -1
  274. package/node_modules/@earendil-works/pi-ai/dist/models.js.map +1 -1
  275. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  276. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js +18 -1
  277. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js.map +1 -1
  278. package/node_modules/@earendil-works/pi-ai/dist/providers/google-vertex.js +2 -1
  279. package/node_modules/@earendil-works/pi-ai/dist/providers/google-vertex.js.map +1 -1
  280. package/node_modules/@earendil-works/pi-ai/dist/providers/google.js +2 -1
  281. package/node_modules/@earendil-works/pi-ai/dist/providers/google.js.map +1 -1
  282. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  283. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js +20 -2
  284. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js.map +1 -1
  285. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +2 -0
  286. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
  287. package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
  288. package/node_modules/@earendil-works/pi-ai/dist/utils/tool-choice-fallback.d.ts +5 -0
  289. package/node_modules/@earendil-works/pi-ai/dist/utils/tool-choice-fallback.d.ts.map +1 -0
  290. package/node_modules/@earendil-works/pi-ai/dist/utils/tool-choice-fallback.js +41 -0
  291. package/node_modules/@earendil-works/pi-ai/dist/utils/tool-choice-fallback.js.map +1 -0
  292. package/node_modules/@earendil-works/pi-ai/package.json +1 -1
  293. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  294. package/npm-shrinkwrap.json +45 -12
  295. package/package.json +8 -4
  296. package/dist/core/extensions/builtin/kimi-web-search/index.d.ts +0 -3
  297. package/dist/core/extensions/builtin/kimi-web-search/index.d.ts.map +0 -1
  298. package/dist/core/extensions/builtin/kimi-web-search/index.js +0 -208
  299. package/dist/core/extensions/builtin/kimi-web-search/index.js.map +0 -1
@@ -0,0 +1,148 @@
1
+ import { InvalidWebfetchUrlError, WebfetchAbortError, WebfetchResponseTooLargeError, WebfetchTimeoutError, } from "./errors.js";
2
+ export const MAX_RESPONSE_SIZE_BYTES = 5 * 1024 * 1024;
3
+ export const DEFAULT_TIMEOUT_SECONDS = 30;
4
+ export const MAX_TIMEOUT_SECONDS = 120;
5
+ const BROWSER_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36";
6
+ export async function fetchUrl(options) {
7
+ validateUrl(options.url);
8
+ const timeoutSeconds = clampTimeout(options.timeoutSeconds);
9
+ const controller = new AbortController();
10
+ const timeout = setTimeout(() => controller.abort(new WebfetchTimeoutError(`Request timed out after ${timeoutSeconds}s`)), timeoutSeconds * 1000);
11
+ const removeAbortForwarder = forwardAbort(options.signal, controller);
12
+ try {
13
+ const response = await fetch(options.url, {
14
+ headers: buildHeaders(options.format, BROWSER_USER_AGENT),
15
+ signal: controller.signal,
16
+ });
17
+ if (response.status === 403 && response.headers.get("cf-mitigated") === "challenge") {
18
+ await cancelBody(response);
19
+ const retry = await fetch(options.url, {
20
+ headers: buildHeaders(options.format, "pi-webfetch"),
21
+ signal: controller.signal,
22
+ });
23
+ return await readFetchResponse(options.url, retry, controller.signal);
24
+ }
25
+ return await readFetchResponse(options.url, response, controller.signal);
26
+ }
27
+ finally {
28
+ clearTimeout(timeout);
29
+ removeAbortForwarder();
30
+ }
31
+ }
32
+ export function validateUrl(url) {
33
+ if (!url.startsWith("http://") && !url.startsWith("https://")) {
34
+ throw new InvalidWebfetchUrlError("URL must start with http:// or https://");
35
+ }
36
+ try {
37
+ new URL(url);
38
+ }
39
+ catch {
40
+ throw new InvalidWebfetchUrlError(`Invalid URL: ${url}`);
41
+ }
42
+ }
43
+ export function clampTimeout(timeoutSeconds) {
44
+ if (timeoutSeconds === undefined)
45
+ return DEFAULT_TIMEOUT_SECONDS;
46
+ if (!Number.isFinite(timeoutSeconds) || timeoutSeconds <= 0)
47
+ return DEFAULT_TIMEOUT_SECONDS;
48
+ return Math.min(Math.ceil(timeoutSeconds), MAX_TIMEOUT_SECONDS);
49
+ }
50
+ export function buildAcceptHeader(format) {
51
+ switch (format) {
52
+ case "markdown":
53
+ return "text/markdown;q=1.0, text/x-markdown;q=0.9, text/plain;q=0.8, text/html;q=0.7, */*;q=0.1";
54
+ case "text":
55
+ return "text/plain;q=1.0, text/markdown;q=0.9, text/html;q=0.8, */*;q=0.1";
56
+ case "html":
57
+ return "text/html;q=1.0, application/xhtml+xml;q=0.9, text/plain;q=0.8, text/markdown;q=0.7, */*;q=0.1";
58
+ }
59
+ }
60
+ function buildHeaders(format, userAgent) {
61
+ return {
62
+ Accept: buildAcceptHeader(format),
63
+ "Accept-Language": "en-US,en;q=0.9",
64
+ "User-Agent": userAgent,
65
+ };
66
+ }
67
+ async function readFetchResponse(url, response, signal) {
68
+ await rejectOversizedContentLength(response);
69
+ const body = await readResponseBody(response, signal);
70
+ return {
71
+ url: response.url || url,
72
+ status: response.status,
73
+ statusText: response.statusText,
74
+ contentType: response.headers.get("content-type") ?? "",
75
+ bytes: body.length,
76
+ body,
77
+ truncated: body.length === MAX_RESPONSE_SIZE_BYTES,
78
+ };
79
+ }
80
+ async function rejectOversizedContentLength(response) {
81
+ const contentLength = response.headers.get("content-length");
82
+ if (contentLength && Number.parseInt(contentLength, 10) > MAX_RESPONSE_SIZE_BYTES) {
83
+ await cancelBody(response);
84
+ throw new WebfetchResponseTooLargeError("Response too large (exceeds 5MB limit)");
85
+ }
86
+ }
87
+ async function cancelBody(response) {
88
+ try {
89
+ await response.body?.cancel();
90
+ }
91
+ catch {
92
+ // Preserve the caller's original failure.
93
+ }
94
+ }
95
+ async function readResponseBody(response, signal) {
96
+ if (!response.body)
97
+ return new Uint8Array();
98
+ const reader = response.body.getReader();
99
+ const chunks = [];
100
+ let total = 0;
101
+ try {
102
+ while (true) {
103
+ if (signal.aborted) {
104
+ await cancelReader(reader);
105
+ throw new WebfetchAbortError("Request aborted");
106
+ }
107
+ const read = await reader.read();
108
+ if (read.done)
109
+ break;
110
+ chunks.push(read.value);
111
+ total += read.value.length;
112
+ if (total > MAX_RESPONSE_SIZE_BYTES) {
113
+ await cancelReader(reader);
114
+ throw new WebfetchResponseTooLargeError("Response too large (exceeds 5MB limit)");
115
+ }
116
+ }
117
+ }
118
+ finally {
119
+ reader.releaseLock();
120
+ }
121
+ const body = new Uint8Array(total);
122
+ let offset = 0;
123
+ for (const chunk of chunks) {
124
+ body.set(chunk, offset);
125
+ offset += chunk.length;
126
+ }
127
+ return body;
128
+ }
129
+ async function cancelReader(reader) {
130
+ try {
131
+ await reader.cancel();
132
+ }
133
+ catch {
134
+ // Preserve the caller's original failure.
135
+ }
136
+ }
137
+ function forwardAbort(signal, controller) {
138
+ if (!signal)
139
+ return () => { };
140
+ if (signal.aborted) {
141
+ controller.abort(signal.reason);
142
+ return () => { };
143
+ }
144
+ const listener = () => controller.abort(signal.reason);
145
+ signal.addEventListener("abort", listener, { once: true });
146
+ return () => signal.removeEventListener("abort", listener);
147
+ }
148
+ //# sourceMappingURL=fetcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetcher.js","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/webfetch/webfetch/fetcher.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tInvalidWebfetchUrlError,\n\tWebfetchAbortError,\n\tWebfetchResponseTooLargeError,\n\tWebfetchTimeoutError,\n} from \"./errors.ts\";\n\nexport const MAX_RESPONSE_SIZE_BYTES = 5 * 1024 * 1024;\nexport const DEFAULT_TIMEOUT_SECONDS = 30;\nexport const MAX_TIMEOUT_SECONDS = 120;\n\nconst BROWSER_USER_AGENT =\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36\";\n\nexport type WebfetchFormat = \"markdown\" | \"text\" | \"html\";\n\nexport interface FetchOptions {\n\turl: string;\n\tformat: WebfetchFormat;\n\ttimeoutSeconds?: number;\n\tsignal?: AbortSignal;\n}\n\nexport interface FetchResult {\n\turl: string;\n\tstatus: number;\n\tstatusText: string;\n\tcontentType: string;\n\tbytes: number;\n\tbody: Uint8Array;\n\ttruncated: boolean;\n}\n\nexport async function fetchUrl(options: FetchOptions): Promise<FetchResult> {\n\tvalidateUrl(options.url);\n\n\tconst timeoutSeconds = clampTimeout(options.timeoutSeconds);\n\tconst controller = new AbortController();\n\tconst timeout = setTimeout(\n\t\t() => controller.abort(new WebfetchTimeoutError(`Request timed out after ${timeoutSeconds}s`)),\n\t\ttimeoutSeconds * 1000,\n\t);\n\tconst removeAbortForwarder = forwardAbort(options.signal, controller);\n\n\ttry {\n\t\tconst response = await fetch(options.url, {\n\t\t\theaders: buildHeaders(options.format, BROWSER_USER_AGENT),\n\t\t\tsignal: controller.signal,\n\t\t});\n\n\t\tif (response.status === 403 && response.headers.get(\"cf-mitigated\") === \"challenge\") {\n\t\t\tawait cancelBody(response);\n\t\t\tconst retry = await fetch(options.url, {\n\t\t\t\theaders: buildHeaders(options.format, \"pi-webfetch\"),\n\t\t\t\tsignal: controller.signal,\n\t\t\t});\n\t\t\treturn await readFetchResponse(options.url, retry, controller.signal);\n\t\t}\n\n\t\treturn await readFetchResponse(options.url, response, controller.signal);\n\t} finally {\n\t\tclearTimeout(timeout);\n\t\tremoveAbortForwarder();\n\t}\n}\n\nexport function validateUrl(url: string): void {\n\tif (!url.startsWith(\"http://\") && !url.startsWith(\"https://\")) {\n\t\tthrow new InvalidWebfetchUrlError(\"URL must start with http:// or https://\");\n\t}\n\n\ttry {\n\t\tnew URL(url);\n\t} catch {\n\t\tthrow new InvalidWebfetchUrlError(`Invalid URL: ${url}`);\n\t}\n}\n\nexport function clampTimeout(timeoutSeconds: number | undefined): number {\n\tif (timeoutSeconds === undefined) return DEFAULT_TIMEOUT_SECONDS;\n\tif (!Number.isFinite(timeoutSeconds) || timeoutSeconds <= 0) return DEFAULT_TIMEOUT_SECONDS;\n\treturn Math.min(Math.ceil(timeoutSeconds), MAX_TIMEOUT_SECONDS);\n}\n\nexport function buildAcceptHeader(format: WebfetchFormat): string {\n\tswitch (format) {\n\t\tcase \"markdown\":\n\t\t\treturn \"text/markdown;q=1.0, text/x-markdown;q=0.9, text/plain;q=0.8, text/html;q=0.7, */*;q=0.1\";\n\t\tcase \"text\":\n\t\t\treturn \"text/plain;q=1.0, text/markdown;q=0.9, text/html;q=0.8, */*;q=0.1\";\n\t\tcase \"html\":\n\t\t\treturn \"text/html;q=1.0, application/xhtml+xml;q=0.9, text/plain;q=0.8, text/markdown;q=0.7, */*;q=0.1\";\n\t}\n}\n\nfunction buildHeaders(format: WebfetchFormat, userAgent: string): Record<string, string> {\n\treturn {\n\t\tAccept: buildAcceptHeader(format),\n\t\t\"Accept-Language\": \"en-US,en;q=0.9\",\n\t\t\"User-Agent\": userAgent,\n\t};\n}\n\nasync function readFetchResponse(url: string, response: Response, signal: AbortSignal): Promise<FetchResult> {\n\tawait rejectOversizedContentLength(response);\n\tconst body = await readResponseBody(response, signal);\n\treturn {\n\t\turl: response.url || url,\n\t\tstatus: response.status,\n\t\tstatusText: response.statusText,\n\t\tcontentType: response.headers.get(\"content-type\") ?? \"\",\n\t\tbytes: body.length,\n\t\tbody,\n\t\ttruncated: body.length === MAX_RESPONSE_SIZE_BYTES,\n\t};\n}\n\nasync function rejectOversizedContentLength(response: Response): Promise<void> {\n\tconst contentLength = response.headers.get(\"content-length\");\n\tif (contentLength && Number.parseInt(contentLength, 10) > MAX_RESPONSE_SIZE_BYTES) {\n\t\tawait cancelBody(response);\n\t\tthrow new WebfetchResponseTooLargeError(\"Response too large (exceeds 5MB limit)\");\n\t}\n}\n\nasync function cancelBody(response: Response): Promise<void> {\n\ttry {\n\t\tawait response.body?.cancel();\n\t} catch {\n\t\t// Preserve the caller's original failure.\n\t}\n}\n\nasync function readResponseBody(response: Response, signal: AbortSignal): Promise<Uint8Array> {\n\tif (!response.body) return new Uint8Array();\n\n\tconst reader = response.body.getReader();\n\tconst chunks: Uint8Array[] = [];\n\tlet total = 0;\n\n\ttry {\n\t\twhile (true) {\n\t\t\tif (signal.aborted) {\n\t\t\t\tawait cancelReader(reader);\n\t\t\t\tthrow new WebfetchAbortError(\"Request aborted\");\n\t\t\t}\n\t\t\tconst read = await reader.read();\n\t\t\tif (read.done) break;\n\t\t\tchunks.push(read.value);\n\t\t\ttotal += read.value.length;\n\t\t\tif (total > MAX_RESPONSE_SIZE_BYTES) {\n\t\t\t\tawait cancelReader(reader);\n\t\t\t\tthrow new WebfetchResponseTooLargeError(\"Response too large (exceeds 5MB limit)\");\n\t\t\t}\n\t\t}\n\t} finally {\n\t\treader.releaseLock();\n\t}\n\n\tconst body = new Uint8Array(total);\n\tlet offset = 0;\n\tfor (const chunk of chunks) {\n\t\tbody.set(chunk, offset);\n\t\toffset += chunk.length;\n\t}\n\treturn body;\n}\n\nasync function cancelReader(reader: ReadableStreamDefaultReader<Uint8Array>): Promise<void> {\n\ttry {\n\t\tawait reader.cancel();\n\t} catch {\n\t\t// Preserve the caller's original failure.\n\t}\n}\n\nfunction forwardAbort(signal: AbortSignal | undefined, controller: AbortController): () => void {\n\tif (!signal) return () => {};\n\tif (signal.aborted) {\n\t\tcontroller.abort(signal.reason);\n\t\treturn () => {};\n\t}\n\n\tconst listener = (): void => controller.abort(signal.reason);\n\tsignal.addEventListener(\"abort\", listener, { once: true });\n\treturn () => signal.removeEventListener(\"abort\", listener);\n}\n"]}
@@ -0,0 +1,15 @@
1
+ import { Text } from "@earendil-works/pi-tui";
2
+ import type { Theme } from "../../../../../modes/interactive/theme/theme.ts";
3
+ import type { ToolRenderResultOptions } from "../../../types.ts";
4
+ import type { WebfetchRenderDetails } from "./tool.ts";
5
+ interface ResultLike<TDetails> {
6
+ content: ReadonlyArray<{
7
+ type: string;
8
+ text?: string;
9
+ }>;
10
+ details?: TDetails;
11
+ }
12
+ export declare function renderWebfetchCall(args: unknown, theme: Theme): Text;
13
+ export declare function renderWebfetchResult(result: ResultLike<WebfetchRenderDetails>, options: ToolRenderResultOptions, theme: Theme): Text;
14
+ export {};
15
+ //# sourceMappingURL=renderers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderers.d.ts","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/webfetch/webfetch/renderers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAmB,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iDAAiD,CAAC;AAC7E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAEjE,OAAO,KAAK,EAA4C,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAYjG,UAAU,UAAU,CAAC,QAAQ;IAC5B,OAAO,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,OAAO,CAAC,EAAE,QAAQ,CAAC;CACnB;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAOpE;AAED,wBAAgB,oBAAoB,CACnC,MAAM,EAAE,UAAU,CAAC,qBAAqB,CAAC,EACzC,OAAO,EAAE,uBAAuB,EAChC,KAAK,EAAE,KAAK,GACV,IAAI,CA0CN"}
@@ -0,0 +1,110 @@
1
+ import { Text, truncateToWidth } from "@earendil-works/pi-tui";
2
+ const URL_BUDGET = 92;
3
+ const PREVIEW_LINES = 4;
4
+ const PREVIEW_WIDTH = 120;
5
+ export function renderWebfetchCall(args, theme) {
6
+ const webfetchArgs = parseWebfetchArgs(args);
7
+ const head = theme.fg("toolTitle", theme.bold("webfetch "));
8
+ const url = theme.fg("accent", shorten(webfetchArgs.url, URL_BUDGET));
9
+ const format = theme.fg("muted", ` [${webfetchArgs.format ?? "markdown"}]`);
10
+ const timeout = webfetchArgs.timeout === undefined ? "" : theme.fg("dim", ` ${webfetchArgs.timeout}s`);
11
+ return new Text(head + url + format + timeout, 0, 0);
12
+ }
13
+ export function renderWebfetchResult(result, options, theme) {
14
+ if (options.isPartial) {
15
+ const details = result.details;
16
+ if (isWebfetchProgressDetails(details)) {
17
+ return new Text(theme.fg("warning", `Fetching ${shorten(details.url, URL_BUDGET)} as ${details.format} (${details.timeoutSeconds}s)`), 0, 0);
18
+ }
19
+ return new Text(theme.fg("warning", "Fetching..."), 0, 0);
20
+ }
21
+ const details = result.details;
22
+ const text = result.content.find((block) => block.type === "text")?.text ?? "";
23
+ if (!isWebfetchDetails(details)) {
24
+ return new Text(theme.fg("muted", truncateToWidth(text, PREVIEW_WIDTH)), 0, 0);
25
+ }
26
+ const statusKey = details.status >= 200 && details.status < 300 ? "success" : "warning";
27
+ const status = theme.fg(statusKey, `${details.status} ${details.statusText || "OK"}`);
28
+ const format = theme.fg("accent", details.format);
29
+ const size = theme.fg("muted", formatBytes(details.bytes));
30
+ const converted = details.converted ? theme.fg("dim", " converted") : "";
31
+ const header = `${status} ${theme.fg("muted", "•")} ${format} ${theme.fg("muted", "•")} ${size}${converted}`;
32
+ if (!options.expanded) {
33
+ const preview = previewText(text, theme);
34
+ return new Text([header, ...preview].join("\n"), 0, 0);
35
+ }
36
+ const lines = [
37
+ header,
38
+ theme.fg("dim", `URL: ${shorten(details.finalUrl, URL_BUDGET)}`),
39
+ theme.fg("dim", `Content-Type: ${details.contentType || "unknown"}`),
40
+ "",
41
+ ...collectLines(text, 24).map((line) => theme.fg("toolOutput", truncateToWidth(line, PREVIEW_WIDTH))),
42
+ ];
43
+ return new Text(lines.join("\n"), 0, 0);
44
+ }
45
+ function collectLines(text, limit) {
46
+ const lines = [];
47
+ let start = 0;
48
+ while (lines.length < limit && start <= text.length) {
49
+ const newlineIndex = text.indexOf("\n", start);
50
+ if (newlineIndex === -1) {
51
+ lines.push(text.slice(start));
52
+ break;
53
+ }
54
+ lines.push(text.slice(start, newlineIndex));
55
+ start = newlineIndex + 1;
56
+ }
57
+ return lines;
58
+ }
59
+ function collectNonEmptyTrimmedLines(text, limit) {
60
+ const lines = [];
61
+ let start = 0;
62
+ while (lines.length < limit && start <= text.length) {
63
+ const newlineIndex = text.indexOf("\n", start);
64
+ const line = (newlineIndex === -1 ? text.slice(start) : text.slice(start, newlineIndex)).trim();
65
+ if (line.length > 0)
66
+ lines.push(line);
67
+ if (newlineIndex === -1)
68
+ break;
69
+ start = newlineIndex + 1;
70
+ }
71
+ return lines;
72
+ }
73
+ function parseWebfetchArgs(args) {
74
+ if (typeof args !== "object" || args === null) {
75
+ return { url: "" };
76
+ }
77
+ const webfetchArgs = {
78
+ url: "url" in args && typeof args.url === "string" ? args.url : "",
79
+ };
80
+ if ("format" in args && typeof args.format === "string")
81
+ webfetchArgs.format = args.format;
82
+ if ("timeout" in args && typeof args.timeout === "number")
83
+ webfetchArgs.timeout = args.timeout;
84
+ return webfetchArgs;
85
+ }
86
+ function isWebfetchProgressDetails(details) {
87
+ return typeof details === "object" && details !== null && "phase" in details && details.phase === "fetching";
88
+ }
89
+ function isWebfetchDetails(details) {
90
+ return typeof details === "object" && details !== null && "status" in details && typeof details.status === "number";
91
+ }
92
+ function previewText(text, theme) {
93
+ const lines = collectNonEmptyTrimmedLines(text, PREVIEW_LINES);
94
+ if (lines.length === 0)
95
+ return [theme.fg("dim", " empty response")];
96
+ return lines.map((line) => theme.fg("toolOutput", ` ${truncateToWidth(line, PREVIEW_WIDTH)}`));
97
+ }
98
+ function shorten(value, max) {
99
+ if (value.length <= max)
100
+ return value;
101
+ return `${value.slice(0, max - 1)}…`;
102
+ }
103
+ function formatBytes(bytes) {
104
+ if (bytes < 1024)
105
+ return `${bytes} B`;
106
+ if (bytes < 1024 * 1024)
107
+ return `${(bytes / 1024).toFixed(1)} KB`;
108
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
109
+ }
110
+ //# sourceMappingURL=renderers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderers.js","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/webfetch/webfetch/renderers.ts"],"names":[],"mappings":"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","sourcesContent":["import { Text, truncateToWidth } from \"@earendil-works/pi-tui\";\nimport type { Theme } from \"../../../../../modes/interactive/theme/theme.ts\";\nimport type { ToolRenderResultOptions } from \"../../../types.ts\";\n\nimport type { WebfetchDetails, WebfetchProgressDetails, WebfetchRenderDetails } from \"./tool.ts\";\n\nconst URL_BUDGET = 92;\nconst PREVIEW_LINES = 4;\nconst PREVIEW_WIDTH = 120;\n\ninterface WebfetchArgs {\n\turl: string;\n\tformat?: string;\n\ttimeout?: number;\n}\n\ninterface ResultLike<TDetails> {\n\tcontent: ReadonlyArray<{ type: string; text?: string }>;\n\tdetails?: TDetails;\n}\n\nexport function renderWebfetchCall(args: unknown, theme: Theme): Text {\n\tconst webfetchArgs = parseWebfetchArgs(args);\n\tconst head = theme.fg(\"toolTitle\", theme.bold(\"webfetch \"));\n\tconst url = theme.fg(\"accent\", shorten(webfetchArgs.url, URL_BUDGET));\n\tconst format = theme.fg(\"muted\", ` [${webfetchArgs.format ?? \"markdown\"}]`);\n\tconst timeout = webfetchArgs.timeout === undefined ? \"\" : theme.fg(\"dim\", ` ${webfetchArgs.timeout}s`);\n\treturn new Text(head + url + format + timeout, 0, 0);\n}\n\nexport function renderWebfetchResult(\n\tresult: ResultLike<WebfetchRenderDetails>,\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n): Text {\n\tif (options.isPartial) {\n\t\tconst details = result.details;\n\t\tif (isWebfetchProgressDetails(details)) {\n\t\t\treturn new Text(\n\t\t\t\ttheme.fg(\n\t\t\t\t\t\"warning\",\n\t\t\t\t\t`Fetching ${shorten(details.url, URL_BUDGET)} as ${details.format} (${details.timeoutSeconds}s)`,\n\t\t\t\t),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t);\n\t\t}\n\t\treturn new Text(theme.fg(\"warning\", \"Fetching...\"), 0, 0);\n\t}\n\n\tconst details = result.details;\n\tconst text = result.content.find((block) => block.type === \"text\")?.text ?? \"\";\n\tif (!isWebfetchDetails(details)) {\n\t\treturn new Text(theme.fg(\"muted\", truncateToWidth(text, PREVIEW_WIDTH)), 0, 0);\n\t}\n\n\tconst statusKey = details.status >= 200 && details.status < 300 ? \"success\" : \"warning\";\n\tconst status = theme.fg(statusKey, `${details.status} ${details.statusText || \"OK\"}`);\n\tconst format = theme.fg(\"accent\", details.format);\n\tconst size = theme.fg(\"muted\", formatBytes(details.bytes));\n\tconst converted = details.converted ? theme.fg(\"dim\", \" converted\") : \"\";\n\tconst header = `${status} ${theme.fg(\"muted\", \"•\")} ${format} ${theme.fg(\"muted\", \"•\")} ${size}${converted}`;\n\n\tif (!options.expanded) {\n\t\tconst preview = previewText(text, theme);\n\t\treturn new Text([header, ...preview].join(\"\\n\"), 0, 0);\n\t}\n\n\tconst lines = [\n\t\theader,\n\t\ttheme.fg(\"dim\", `URL: ${shorten(details.finalUrl, URL_BUDGET)}`),\n\t\ttheme.fg(\"dim\", `Content-Type: ${details.contentType || \"unknown\"}`),\n\t\t\"\",\n\t\t...collectLines(text, 24).map((line) => theme.fg(\"toolOutput\", truncateToWidth(line, PREVIEW_WIDTH))),\n\t];\n\treturn new Text(lines.join(\"\\n\"), 0, 0);\n}\n\nfunction collectLines(text: string, limit: number): string[] {\n\tconst lines: string[] = [];\n\tlet start = 0;\n\twhile (lines.length < limit && start <= text.length) {\n\t\tconst newlineIndex = text.indexOf(\"\\n\", start);\n\t\tif (newlineIndex === -1) {\n\t\t\tlines.push(text.slice(start));\n\t\t\tbreak;\n\t\t}\n\t\tlines.push(text.slice(start, newlineIndex));\n\t\tstart = newlineIndex + 1;\n\t}\n\treturn lines;\n}\n\nfunction collectNonEmptyTrimmedLines(text: string, limit: number): string[] {\n\tconst lines: string[] = [];\n\tlet start = 0;\n\twhile (lines.length < limit && start <= text.length) {\n\t\tconst newlineIndex = text.indexOf(\"\\n\", start);\n\t\tconst line = (newlineIndex === -1 ? text.slice(start) : text.slice(start, newlineIndex)).trim();\n\t\tif (line.length > 0) lines.push(line);\n\t\tif (newlineIndex === -1) break;\n\t\tstart = newlineIndex + 1;\n\t}\n\treturn lines;\n}\n\nfunction parseWebfetchArgs(args: unknown): WebfetchArgs {\n\tif (typeof args !== \"object\" || args === null) {\n\t\treturn { url: \"\" };\n\t}\n\tconst webfetchArgs: WebfetchArgs = {\n\t\turl: \"url\" in args && typeof args.url === \"string\" ? args.url : \"\",\n\t};\n\tif (\"format\" in args && typeof args.format === \"string\") webfetchArgs.format = args.format;\n\tif (\"timeout\" in args && typeof args.timeout === \"number\") webfetchArgs.timeout = args.timeout;\n\treturn webfetchArgs;\n}\n\nfunction isWebfetchProgressDetails(details: WebfetchRenderDetails | unknown): details is WebfetchProgressDetails {\n\treturn typeof details === \"object\" && details !== null && \"phase\" in details && details.phase === \"fetching\";\n}\n\nfunction isWebfetchDetails(details: WebfetchRenderDetails | unknown): details is WebfetchDetails {\n\treturn typeof details === \"object\" && details !== null && \"status\" in details && typeof details.status === \"number\";\n}\n\nfunction previewText(text: string, theme: Theme): string[] {\n\tconst lines = collectNonEmptyTrimmedLines(text, PREVIEW_LINES);\n\tif (lines.length === 0) return [theme.fg(\"dim\", \" empty response\")];\n\treturn lines.map((line) => theme.fg(\"toolOutput\", ` ${truncateToWidth(line, PREVIEW_WIDTH)}`));\n}\n\nfunction shorten(value: string, max: number): string {\n\tif (value.length <= max) return value;\n\treturn `${value.slice(0, max - 1)}…`;\n}\n\nfunction formatBytes(bytes: number): string {\n\tif (bytes < 1024) return `${bytes} B`;\n\tif (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n\treturn `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n"]}
@@ -0,0 +1,28 @@
1
+ import { Type } from "typebox";
2
+ import { type WebfetchFormat } from "./fetcher.ts";
3
+ export interface WebfetchDetails {
4
+ url: string;
5
+ finalUrl: string;
6
+ format: WebfetchFormat;
7
+ status: number;
8
+ statusText: string;
9
+ contentType: string;
10
+ bytes: number;
11
+ timeoutSeconds: number;
12
+ converted: boolean;
13
+ truncated: boolean;
14
+ }
15
+ export interface WebfetchProgressDetails {
16
+ phase: "fetching";
17
+ url: string;
18
+ format: WebfetchFormat;
19
+ timeoutSeconds: number;
20
+ }
21
+ export type WebfetchRenderDetails = WebfetchDetails | WebfetchProgressDetails;
22
+ export declare const webfetch: import("../../../types.ts").ToolDefinition<Type.TObject<{
23
+ url: Type.TString;
24
+ format: Type.TOptional<Type.TUnsafe<"html" | "markdown" | "text">>;
25
+ timeout: Type.TOptional<Type.TNumber>;
26
+ }>, WebfetchRenderDetails, any> & import("../../../types.ts").ToolDefinition<any, any, any>;
27
+ export declare function parseWebfetchFormat(value: unknown): WebfetchFormat;
28
+ //# sourceMappingURL=tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/webfetch/webfetch/tool.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAI/B,OAAO,EAA0B,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAc3E,MAAM,WAAW,eAAe;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,uBAAuB;IACvC,KAAK,EAAE,UAAU,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,cAAc,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,qBAAqB,GAAG,eAAe,GAAG,uBAAuB,CAAC;AAE9E,eAAO,MAAM,QAAQ;;;;2FA2DnB,CAAC;AAEH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAMlE"}
@@ -0,0 +1,79 @@
1
+ import { StringEnum } from "@earendil-works/pi-ai";
2
+ import { Type } from "typebox";
3
+ import { defineTool } from "../../../types.js";
4
+ import { htmlToMarkdown, htmlToText } from "./content.js";
5
+ import { clampTimeout, fetchUrl } from "./fetcher.js";
6
+ const WEBFETCH_FORMATS = ["markdown", "text", "html"];
7
+ const Params = Type.Object({
8
+ url: Type.String({ description: "The URL to fetch content from" }),
9
+ format: Type.Optional(StringEnum(["markdown", "text", "html"], {
10
+ description: "The format to return the content in. Defaults to markdown.",
11
+ })),
12
+ timeout: Type.Optional(Type.Number({ description: "Optional timeout in seconds. Maximum 120." })),
13
+ });
14
+ export const webfetch = defineTool({
15
+ name: "webfetch",
16
+ label: "Web Fetch",
17
+ description: "Fetches content from a URL and returns it as markdown, plain text, or HTML. " +
18
+ "Network use is bounded by timeout and response size limits.",
19
+ promptSnippet: "webfetch: retrieve URL content as markdown, text, or html",
20
+ promptGuidelines: [
21
+ "Use webfetch when a specific URL must be retrieved.",
22
+ "Prefer markdown format unless raw HTML or plain text is explicitly needed.",
23
+ "The tool is read-only and does not modify files.",
24
+ ],
25
+ parameters: Params,
26
+ async execute(_toolCallId, params, signal, onUpdate, _ctx) {
27
+ const format = parseWebfetchFormat(params.format);
28
+ const timeoutSeconds = clampTimeout(params.timeout);
29
+ onUpdate?.({
30
+ content: [{ type: "text", text: `Fetching ${params.url} as ${format} (timeout ${timeoutSeconds}s)` }],
31
+ details: { phase: "fetching", url: params.url, format, timeoutSeconds },
32
+ });
33
+ const fetched = await fetchUrl({
34
+ url: params.url,
35
+ format,
36
+ timeoutSeconds,
37
+ ...(signal === undefined ? {} : { signal }),
38
+ });
39
+ const raw = new TextDecoder().decode(fetched.body);
40
+ const contentType = fetched.contentType.toLowerCase();
41
+ const isHtml = contentType.includes("text/html") || contentType.includes("application/xhtml+xml");
42
+ let text = raw;
43
+ let converted = false;
44
+ if (isHtml && format === "markdown") {
45
+ text = htmlToMarkdown(raw);
46
+ converted = true;
47
+ }
48
+ else if (isHtml && format === "text") {
49
+ text = htmlToText(raw);
50
+ converted = true;
51
+ }
52
+ const details = {
53
+ url: params.url,
54
+ finalUrl: fetched.url,
55
+ format,
56
+ status: fetched.status,
57
+ statusText: fetched.statusText,
58
+ contentType: fetched.contentType,
59
+ bytes: fetched.bytes,
60
+ timeoutSeconds,
61
+ converted,
62
+ truncated: fetched.truncated,
63
+ };
64
+ return {
65
+ content: [{ type: "text", text }],
66
+ details,
67
+ };
68
+ },
69
+ });
70
+ export function parseWebfetchFormat(value) {
71
+ if (value === undefined)
72
+ return "markdown";
73
+ for (const format of WEBFETCH_FORMATS) {
74
+ if (value === format)
75
+ return format;
76
+ }
77
+ return "markdown";
78
+ }
79
+ //# sourceMappingURL=tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.js","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/webfetch/webfetch/tool.ts"],"names":[],"mappings":"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","sourcesContent":["import { StringEnum } from \"@earendil-works/pi-ai\";\nimport { Type } from \"typebox\";\nimport { defineTool } from \"../../../types.ts\";\n\nimport { htmlToMarkdown, htmlToText } from \"./content.ts\";\nimport { clampTimeout, fetchUrl, type WebfetchFormat } from \"./fetcher.ts\";\n\nconst WEBFETCH_FORMATS = [\"markdown\", \"text\", \"html\"] as const;\n\nconst Params = Type.Object({\n\turl: Type.String({ description: \"The URL to fetch content from\" }),\n\tformat: Type.Optional(\n\t\tStringEnum([\"markdown\", \"text\", \"html\"] as const, {\n\t\t\tdescription: \"The format to return the content in. Defaults to markdown.\",\n\t\t}),\n\t),\n\ttimeout: Type.Optional(Type.Number({ description: \"Optional timeout in seconds. Maximum 120.\" })),\n});\n\nexport interface WebfetchDetails {\n\turl: string;\n\tfinalUrl: string;\n\tformat: WebfetchFormat;\n\tstatus: number;\n\tstatusText: string;\n\tcontentType: string;\n\tbytes: number;\n\ttimeoutSeconds: number;\n\tconverted: boolean;\n\ttruncated: boolean;\n}\n\nexport interface WebfetchProgressDetails {\n\tphase: \"fetching\";\n\turl: string;\n\tformat: WebfetchFormat;\n\ttimeoutSeconds: number;\n}\n\nexport type WebfetchRenderDetails = WebfetchDetails | WebfetchProgressDetails;\n\nexport const webfetch = defineTool<typeof Params, WebfetchRenderDetails>({\n\tname: \"webfetch\",\n\tlabel: \"Web Fetch\",\n\tdescription:\n\t\t\"Fetches content from a URL and returns it as markdown, plain text, or HTML. \" +\n\t\t\"Network use is bounded by timeout and response size limits.\",\n\tpromptSnippet: \"webfetch: retrieve URL content as markdown, text, or html\",\n\tpromptGuidelines: [\n\t\t\"Use webfetch when a specific URL must be retrieved.\",\n\t\t\"Prefer markdown format unless raw HTML or plain text is explicitly needed.\",\n\t\t\"The tool is read-only and does not modify files.\",\n\t],\n\tparameters: Params,\n\tasync execute(_toolCallId, params, signal, onUpdate, _ctx) {\n\t\tconst format = parseWebfetchFormat(params.format);\n\t\tconst timeoutSeconds = clampTimeout(params.timeout);\n\t\tonUpdate?.({\n\t\t\tcontent: [{ type: \"text\", text: `Fetching ${params.url} as ${format} (timeout ${timeoutSeconds}s)` }],\n\t\t\tdetails: { phase: \"fetching\", url: params.url, format, timeoutSeconds },\n\t\t});\n\n\t\tconst fetched = await fetchUrl({\n\t\t\turl: params.url,\n\t\t\tformat,\n\t\t\ttimeoutSeconds,\n\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t});\n\t\tconst raw = new TextDecoder().decode(fetched.body);\n\t\tconst contentType = fetched.contentType.toLowerCase();\n\t\tconst isHtml = contentType.includes(\"text/html\") || contentType.includes(\"application/xhtml+xml\");\n\t\tlet text = raw;\n\t\tlet converted = false;\n\n\t\tif (isHtml && format === \"markdown\") {\n\t\t\ttext = htmlToMarkdown(raw);\n\t\t\tconverted = true;\n\t\t} else if (isHtml && format === \"text\") {\n\t\t\ttext = htmlToText(raw);\n\t\t\tconverted = true;\n\t\t}\n\n\t\tconst details: WebfetchDetails = {\n\t\t\turl: params.url,\n\t\t\tfinalUrl: fetched.url,\n\t\t\tformat,\n\t\t\tstatus: fetched.status,\n\t\t\tstatusText: fetched.statusText,\n\t\t\tcontentType: fetched.contentType,\n\t\t\tbytes: fetched.bytes,\n\t\t\ttimeoutSeconds,\n\t\t\tconverted,\n\t\t\ttruncated: fetched.truncated,\n\t\t};\n\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\tdetails,\n\t\t};\n\t},\n});\n\nexport function parseWebfetchFormat(value: unknown): WebfetchFormat {\n\tif (value === undefined) return \"markdown\";\n\tfor (const format of WEBFETCH_FORMATS) {\n\t\tif (value === format) return format;\n\t}\n\treturn \"markdown\";\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import type { ExtensionAPI } from "../../types.ts";
2
+ export default function (pi: ExtensionAPI): void;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/websearch/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,gBAAgB,CAAC;AA2BrE,MAAM,CAAC,OAAO,WAAW,EAAE,EAAE,YAAY,GAAG,IAAI,CAuE/C"}
@@ -0,0 +1,76 @@
1
+ import { loadWebsearchConfig } from "./websearch/config.js";
2
+ import { createWebSearchTool } from "./websearch/tool.js";
3
+ const STATUS_KEY = "pi-websearch";
4
+ const WIDGET_KEY = "pi-websearch";
5
+ const NATIVE_BYPASS_MESSAGE = "Native provider web search is handled by the built-in provider extension.";
6
+ function isProviderNativeBypass(model) {
7
+ // kimi-coding uses the anthropic-messages protocol but its search is the kimi provider (native route), not Anthropic-native; must not be bypassed.
8
+ if (model?.provider === "kimi-coding")
9
+ return false;
10
+ return (model?.provider === "openai" ||
11
+ model?.provider === "anthropic" ||
12
+ model?.api === "anthropic-messages" ||
13
+ model?.api === "openai-responses" ||
14
+ model?.api === "azure-openai-responses");
15
+ }
16
+ export default function (pi) {
17
+ let state = {
18
+ ok: false,
19
+ reason: "missing_config",
20
+ message: "Missing websearch config. Create .pi/websearch.json or ~/.pi/websearch.json before starting pi.",
21
+ };
22
+ function providerLabel(provider) {
23
+ return provider.id ? `${provider.id}/${provider.provider}` : provider.provider;
24
+ }
25
+ function providerList(config) {
26
+ return config.providers.map(providerLabel).join(", ");
27
+ }
28
+ function clearUi(ctx) {
29
+ if (ctx.hasUI === false)
30
+ return;
31
+ ctx.ui.setStatus(STATUS_KEY, undefined);
32
+ ctx.ui.setWidget(WIDGET_KEY, undefined);
33
+ }
34
+ function updateUi(ctx) {
35
+ if (ctx.hasUI === false)
36
+ return;
37
+ if (state.ok) {
38
+ clearUi(ctx);
39
+ return;
40
+ }
41
+ if (state.reason === "provider_native_bypass") {
42
+ clearUi(ctx);
43
+ return;
44
+ }
45
+ clearUi(ctx);
46
+ ctx.ui.notify(state.message, "error");
47
+ }
48
+ pi.registerTool(createWebSearchTool(() => state));
49
+ pi.on("session_start", async (_event, ctx) => {
50
+ state = isProviderNativeBypass(ctx.model)
51
+ ? { ok: false, reason: "provider_native_bypass", message: NATIVE_BYPASS_MESSAGE }
52
+ : await loadWebsearchConfig({ cwd: ctx.cwd });
53
+ updateUi(ctx);
54
+ });
55
+ pi.on("session_shutdown", async (_event, ctx) => {
56
+ clearUi(ctx);
57
+ });
58
+ pi.registerCommand("websearch", {
59
+ description: "Show web search provider status",
60
+ handler: async (rawArgs, ctx) => {
61
+ const args = rawArgs.trim();
62
+ if (args !== "" && args !== "status") {
63
+ ctx.ui.notify("Usage: /websearch status", "warning");
64
+ return;
65
+ }
66
+ if (state.ok) {
67
+ ctx.ui.notify(`Web search active: strategy=${state.config.strategy}, auto=${state.config.auto ? "enabled" : "disabled"}, providers=${providerList(state.config)}`, "info");
68
+ return;
69
+ }
70
+ ctx.ui.notify(state.reason === "provider_native_bypass"
71
+ ? `Web search deferred: ${state.message}`
72
+ : `Web search inactive: ${state.message}`, state.reason === "provider_native_bypass" ? "info" : "error");
73
+ },
74
+ });
75
+ }
76
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/websearch/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ExtensionAPI, ExtensionContext } from \"../../types.ts\";\n\nimport { loadWebsearchConfig } from \"./websearch/config.ts\";\nimport { createWebSearchTool } from \"./websearch/tool.ts\";\nimport type { ConfigLoadResult, WebsearchConfig } from \"./websearch/types.ts\";\n\nconst STATUS_KEY = \"pi-websearch\";\nconst WIDGET_KEY = \"pi-websearch\";\nconst NATIVE_BYPASS_MESSAGE = \"Native provider web search is handled by the built-in provider extension.\";\n\ntype ProviderModelContext = {\n\tprovider?: string;\n\tapi?: string;\n};\n\nfunction isProviderNativeBypass(model: ProviderModelContext | undefined): boolean {\n\t// kimi-coding uses the anthropic-messages protocol but its search is the kimi provider (native route), not Anthropic-native; must not be bypassed.\n\tif (model?.provider === \"kimi-coding\") return false;\n\treturn (\n\t\tmodel?.provider === \"openai\" ||\n\t\tmodel?.provider === \"anthropic\" ||\n\t\tmodel?.api === \"anthropic-messages\" ||\n\t\tmodel?.api === \"openai-responses\" ||\n\t\tmodel?.api === \"azure-openai-responses\"\n\t);\n}\n\nexport default function (pi: ExtensionAPI): void {\n\tlet state: ConfigLoadResult = {\n\t\tok: false,\n\t\treason: \"missing_config\",\n\t\tmessage: \"Missing websearch config. Create .pi/websearch.json or ~/.pi/websearch.json before starting pi.\",\n\t};\n\n\tfunction providerLabel(provider: WebsearchConfig[\"providers\"][number]): string {\n\t\treturn provider.id ? `${provider.id}/${provider.provider}` : provider.provider;\n\t}\n\n\tfunction providerList(config: WebsearchConfig): string {\n\t\treturn config.providers.map(providerLabel).join(\", \");\n\t}\n\n\tfunction clearUi(ctx: ExtensionContext): void {\n\t\tif (ctx.hasUI === false) return;\n\t\tctx.ui.setStatus(STATUS_KEY, undefined);\n\t\tctx.ui.setWidget(WIDGET_KEY, undefined);\n\t}\n\n\tfunction updateUi(ctx: ExtensionContext): void {\n\t\tif (ctx.hasUI === false) return;\n\t\tif (state.ok) {\n\t\t\tclearUi(ctx);\n\t\t\treturn;\n\t\t}\n\t\tif (state.reason === \"provider_native_bypass\") {\n\t\t\tclearUi(ctx);\n\t\t\treturn;\n\t\t}\n\t\tclearUi(ctx);\n\t\tctx.ui.notify(state.message, \"error\");\n\t}\n\n\tpi.registerTool(createWebSearchTool(() => state));\n\n\tpi.on(\"session_start\", async (_event, ctx) => {\n\t\tstate = isProviderNativeBypass(ctx.model)\n\t\t\t? { ok: false, reason: \"provider_native_bypass\", message: NATIVE_BYPASS_MESSAGE }\n\t\t\t: await loadWebsearchConfig({ cwd: ctx.cwd });\n\t\tupdateUi(ctx);\n\t});\n\n\tpi.on(\"session_shutdown\", async (_event, ctx) => {\n\t\tclearUi(ctx);\n\t});\n\n\tpi.registerCommand(\"websearch\", {\n\t\tdescription: \"Show web search provider status\",\n\t\thandler: async (rawArgs, ctx) => {\n\t\t\tconst args = rawArgs.trim();\n\t\t\tif (args !== \"\" && args !== \"status\") {\n\t\t\t\tctx.ui.notify(\"Usage: /websearch status\", \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (state.ok) {\n\t\t\t\tctx.ui.notify(\n\t\t\t\t\t`Web search active: strategy=${state.config.strategy}, auto=${state.config.auto ? \"enabled\" : \"disabled\"}, providers=${providerList(state.config)}`,\n\t\t\t\t\t\"info\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tctx.ui.notify(\n\t\t\t\tstate.reason === \"provider_native_bypass\"\n\t\t\t\t\t? `Web search deferred: ${state.message}`\n\t\t\t\t\t: `Web search inactive: ${state.message}`,\n\t\t\t\tstate.reason === \"provider_native_bypass\" ? \"info\" : \"error\",\n\t\t\t);\n\t\t},\n\t});\n}\n"]}
@@ -0,0 +1,12 @@
1
+ import type { ConfigLoadResult, ProviderValidationResult, SearchProviderEntry, WebsearchConfig } from "./types.ts";
2
+ export interface ConfigLoadOptions {
3
+ cwd: string;
4
+ homeDir?: string;
5
+ }
6
+ export declare function validateProviderConfig(config: SearchProviderEntry): ProviderValidationResult;
7
+ export declare function validateWebsearchConfig(config: WebsearchConfig): ProviderValidationResult | {
8
+ ok: true;
9
+ config: WebsearchConfig;
10
+ };
11
+ export declare function loadWebsearchConfig(options: ConfigLoadOptions): Promise<ConfigLoadResult>;
12
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/websearch/websearch/config.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAEX,gBAAgB,EAGhB,wBAAwB,EAKxB,mBAAmB,EAEnB,eAAe,EACf,MAAM,YAAY,CAAC;AA2BpB,MAAM,WAAW,iBAAiB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AA8HD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,mBAAmB,GAAG,wBAAwB,CA+C5F;AAED,wBAAgB,uBAAuB,CACtC,MAAM,EAAE,eAAe,GACrB,wBAAwB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,CAiBlE;AAWD,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAwB/F"}