larkup 0.1.41 → 0.1.42

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 (322) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +3 -3
  3. package/.next/diagnostics/route-bundle-stats.json +21 -21
  4. package/.next/fallback-build-manifest.json +3 -3
  5. package/.next/required-server-files.js +1 -1
  6. package/.next/required-server-files.json +1 -1
  7. package/.next/server/app/_global-error.html +1 -1
  8. package/.next/server/app/_global-error.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  14. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  15. package/.next/server/app/_not-found.html +1 -1
  16. package/.next/server/app/_not-found.rsc +18 -18
  17. package/.next/server/app/_not-found.segments/_full.segment.rsc +18 -18
  18. package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  19. package/.next/server/app/_not-found.segments/_index.segment.rsc +12 -12
  20. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  21. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  22. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  23. package/.next/server/app/analytics/page_client-reference-manifest.js +1 -1
  24. package/.next/server/app/analytics.html +1 -1
  25. package/.next/server/app/analytics.rsc +19 -19
  26. package/.next/server/app/analytics.segments/_full.segment.rsc +19 -19
  27. package/.next/server/app/analytics.segments/_head.segment.rsc +4 -4
  28. package/.next/server/app/analytics.segments/_index.segment.rsc +12 -12
  29. package/.next/server/app/analytics.segments/_tree.segment.rsc +2 -2
  30. package/.next/server/app/analytics.segments/analytics/__PAGE__.segment.rsc +3 -3
  31. package/.next/server/app/analytics.segments/analytics.segment.rsc +3 -3
  32. package/.next/server/app/api/analytics/route.js.nft.json +1 -1
  33. package/.next/server/app/api/chat/route.js +1 -1
  34. package/.next/server/app/api/chat/route.js.nft.json +1 -1
  35. package/.next/server/app/api/chat/status/route.js.nft.json +1 -1
  36. package/.next/server/app/api/chat/suggestions/route.js.nft.json +1 -1
  37. package/.next/server/app/api/config/route.js.nft.json +1 -1
  38. package/.next/server/app/api/config/test/route.js.nft.json +1 -1
  39. package/.next/server/app/api/connections/route.js.nft.json +1 -1
  40. package/.next/server/app/api/connections/webhook/route.js.nft.json +1 -1
  41. package/.next/server/app/api/demo/route.js.nft.json +1 -1
  42. package/.next/server/app/api/deploy/cloud-project/route.js.nft.json +1 -1
  43. package/.next/server/app/api/deploy/ssh/route.js.nft.json +1 -1
  44. package/.next/server/app/api/deployment/blob-setup/route.js.nft.json +1 -1
  45. package/.next/server/app/api/deployment/config/route.js.nft.json +1 -1
  46. package/.next/server/app/api/describe-image/route.js.nft.json +1 -1
  47. package/.next/server/app/api/documents/route.js.nft.json +1 -1
  48. package/.next/server/app/api/firecrawl/local/route.js.nft.json +1 -1
  49. package/.next/server/app/api/index/route.js.nft.json +1 -1
  50. package/.next/server/app/api/jobs/[id]/route.js.nft.json +1 -1
  51. package/.next/server/app/api/jobs/route.js.nft.json +1 -1
  52. package/.next/server/app/api/media/[id]/route.js.nft.json +1 -1
  53. package/.next/server/app/api/media/process/route.js.nft.json +1 -1
  54. package/.next/server/app/api/media/route.js.nft.json +1 -1
  55. package/.next/server/app/api/media/stream/route.js.nft.json +1 -1
  56. package/.next/server/app/api/search/bing/route.js.nft.json +1 -1
  57. package/.next/server/app/api/search/brave/route.js.nft.json +1 -1
  58. package/.next/server/app/api/search/exa/route.js.nft.json +1 -1
  59. package/.next/server/app/api/search/google/route.js.nft.json +1 -1
  60. package/.next/server/app/api/search/route.js.nft.json +1 -1
  61. package/.next/server/app/api/search/tavily/route.js.nft.json +1 -1
  62. package/.next/server/app/api/server/agent/route.js.nft.json +1 -1
  63. package/.next/server/app/api/server/download/route.js.nft.json +1 -1
  64. package/.next/server/app/api/server/generate/route.js.nft.json +1 -1
  65. package/.next/server/app/api/server/local/route.js.nft.json +1 -1
  66. package/.next/server/app/api/servers/route.js.nft.json +1 -1
  67. package/.next/server/app/api/tabular/query/route.js.nft.json +1 -1
  68. package/.next/server/app/api/tabular/route.js.nft.json +1 -1
  69. package/.next/server/app/api/upload-file/route.js.nft.json +1 -1
  70. package/.next/server/app/api/uploads/[...slug]/route.js.nft.json +1 -1
  71. package/.next/server/app/chat/page_client-reference-manifest.js +1 -1
  72. package/.next/server/app/chat.html +1 -1
  73. package/.next/server/app/chat.rsc +19 -19
  74. package/.next/server/app/chat.segments/_full.segment.rsc +19 -19
  75. package/.next/server/app/chat.segments/_head.segment.rsc +4 -4
  76. package/.next/server/app/chat.segments/_index.segment.rsc +12 -12
  77. package/.next/server/app/chat.segments/_tree.segment.rsc +2 -2
  78. package/.next/server/app/chat.segments/chat/__PAGE__.segment.rsc +3 -3
  79. package/.next/server/app/chat.segments/chat.segment.rsc +3 -3
  80. package/.next/server/app/data/page.js.nft.json +1 -1
  81. package/.next/server/app/data/page_client-reference-manifest.js +1 -1
  82. package/.next/server/app/data.html +1 -1
  83. package/.next/server/app/data.rsc +19 -19
  84. package/.next/server/app/data.segments/_full.segment.rsc +19 -19
  85. package/.next/server/app/data.segments/_head.segment.rsc +4 -4
  86. package/.next/server/app/data.segments/_index.segment.rsc +12 -12
  87. package/.next/server/app/data.segments/_tree.segment.rsc +2 -2
  88. package/.next/server/app/data.segments/data/__PAGE__.segment.rsc +3 -3
  89. package/.next/server/app/data.segments/data.segment.rsc +3 -3
  90. package/.next/server/app/index.html +1 -1
  91. package/.next/server/app/index.rsc +17 -17
  92. package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  93. package/.next/server/app/index.segments/_full.segment.rsc +17 -17
  94. package/.next/server/app/index.segments/_head.segment.rsc +4 -4
  95. package/.next/server/app/index.segments/_index.segment.rsc +12 -12
  96. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  97. package/.next/server/app/page_client-reference-manifest.js +1 -1
  98. package/.next/server/app/settings/page.js.nft.json +1 -1
  99. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  100. package/.next/server/app/settings.html +1 -1
  101. package/.next/server/app/settings.rsc +19 -19
  102. package/.next/server/app/settings.segments/_full.segment.rsc +19 -19
  103. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  104. package/.next/server/app/settings.segments/_index.segment.rsc +12 -12
  105. package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  106. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  107. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  108. package/.next/server/chunks/0598_next_dist_esm_build_templates_app-route_0kbcuk-.js +9 -7
  109. package/.next/server/chunks/0598_next_dist_esm_build_templates_app-route_0sbf4ny.js +2 -2
  110. package/.next/server/chunks/[root-of-the-server]__06gca-c._.js +1 -1
  111. package/.next/server/chunks/{[root-of-the-server]__1idgvjh._.js → [root-of-the-server]__0pmypq8._.js} +3 -3
  112. package/.next/server/chunks/[root-of-the-server]__0tlfusr._.js +1 -1
  113. package/.next/server/chunks/[root-of-the-server]__0x_a1jw._.js +1 -1
  114. package/.next/server/chunks/[root-of-the-server]__1-f7u-_._.js +1 -1
  115. package/.next/{standalone/apps/web/.next/server/chunks/[root-of-the-server]__1lhls88._.js → server/chunks/[root-of-the-server]__13xnq6l._.js} +1 -1
  116. package/.next/server/chunks/[root-of-the-server]__16jk_se._.js +1 -1
  117. package/.next/server/chunks/[root-of-the-server]__1s_l1di._.js +1 -1
  118. package/.next/server/chunks/[root-of-the-server]__1u4frv-._.js +1 -1
  119. package/.next/server/chunks/[root-of-the-server]__1w00ho9._.js +1 -1
  120. package/.next/server/chunks/_1ncm_ks._.js +1 -1
  121. package/.next/server/chunks/_1sx94ue._.js +1 -1
  122. package/.next/server/chunks/_20il040._.js +1 -1
  123. package/.next/server/chunks/packages_0cc5bxf._.js +1 -1
  124. package/.next/server/chunks/packages_1ggqion._.js +1 -1
  125. package/.next/server/chunks/packages_core_src_embeddings_registry_ts_05dafmb._.js +1 -1
  126. package/.next/server/chunks/ssr/[root-of-the-server]__1k1mxo2._.js +1 -1
  127. package/.next/server/chunks/ssr/_0ffb2sn._.js +1 -1
  128. package/.next/server/chunks/ssr/_169pdlf._.js +2 -2
  129. package/.next/server/chunks/ssr/_18jagqc._.js +1 -1
  130. package/.next/server/chunks/ssr/_1b5ufkw._.js +3 -0
  131. package/.next/server/chunks/ssr/_1yqlx1m._.js +1 -1
  132. package/.next/server/chunks/ssr/packages_core_src_generator_generate-server_ts_15zzrn2._.js +1 -1
  133. package/.next/server/middleware-build-manifest.js +3 -3
  134. package/.next/server/pages/404.html +1 -1
  135. package/.next/server/pages/500.html +1 -1
  136. package/.next/standalone/apps/web/.next/BUILD_ID +1 -1
  137. package/.next/standalone/apps/web/.next/build-manifest.json +3 -3
  138. package/.next/standalone/apps/web/.next/required-server-files.json +1 -1
  139. package/.next/standalone/apps/web/.next/server/app/_global-error.html +1 -1
  140. package/.next/standalone/apps/web/.next/server/app/_global-error.rsc +1 -1
  141. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  142. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  143. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  144. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  145. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  146. package/.next/standalone/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  147. package/.next/standalone/apps/web/.next/server/app/_not-found.html +1 -1
  148. package/.next/standalone/apps/web/.next/server/app/_not-found.rsc +18 -18
  149. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_full.segment.rsc +18 -18
  150. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  151. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_index.segment.rsc +12 -12
  152. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  153. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  154. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  155. package/.next/standalone/apps/web/.next/server/app/analytics/page_client-reference-manifest.js +1 -1
  156. package/.next/standalone/apps/web/.next/server/app/analytics.html +1 -1
  157. package/.next/standalone/apps/web/.next/server/app/analytics.rsc +19 -19
  158. package/.next/standalone/apps/web/.next/server/app/analytics.segments/_full.segment.rsc +19 -19
  159. package/.next/standalone/apps/web/.next/server/app/analytics.segments/_head.segment.rsc +4 -4
  160. package/.next/standalone/apps/web/.next/server/app/analytics.segments/_index.segment.rsc +12 -12
  161. package/.next/standalone/apps/web/.next/server/app/analytics.segments/_tree.segment.rsc +2 -2
  162. package/.next/standalone/apps/web/.next/server/app/analytics.segments/analytics/__PAGE__.segment.rsc +3 -3
  163. package/.next/standalone/apps/web/.next/server/app/analytics.segments/analytics.segment.rsc +3 -3
  164. package/.next/standalone/apps/web/.next/server/app/api/analytics/route.js.nft.json +1 -1
  165. package/.next/standalone/apps/web/.next/server/app/api/chat/route.js +1 -1
  166. package/.next/standalone/apps/web/.next/server/app/api/chat/route.js.nft.json +1 -1
  167. package/.next/standalone/apps/web/.next/server/app/api/chat/status/route.js.nft.json +1 -1
  168. package/.next/standalone/apps/web/.next/server/app/api/chat/suggestions/route.js.nft.json +1 -1
  169. package/.next/standalone/apps/web/.next/server/app/api/config/route.js.nft.json +1 -1
  170. package/.next/standalone/apps/web/.next/server/app/api/config/test/route.js.nft.json +1 -1
  171. package/.next/standalone/apps/web/.next/server/app/api/connections/route.js.nft.json +1 -1
  172. package/.next/standalone/apps/web/.next/server/app/api/connections/webhook/route.js.nft.json +1 -1
  173. package/.next/standalone/apps/web/.next/server/app/api/demo/route.js.nft.json +1 -1
  174. package/.next/standalone/apps/web/.next/server/app/api/deploy/cloud-project/route.js.nft.json +1 -1
  175. package/.next/standalone/apps/web/.next/server/app/api/deploy/ssh/route.js.nft.json +1 -1
  176. package/.next/standalone/apps/web/.next/server/app/api/deployment/blob-setup/route.js.nft.json +1 -1
  177. package/.next/standalone/apps/web/.next/server/app/api/deployment/config/route.js.nft.json +1 -1
  178. package/.next/standalone/apps/web/.next/server/app/api/describe-image/route.js.nft.json +1 -1
  179. package/.next/standalone/apps/web/.next/server/app/api/documents/route.js.nft.json +1 -1
  180. package/.next/standalone/apps/web/.next/server/app/api/firecrawl/local/route.js.nft.json +1 -1
  181. package/.next/standalone/apps/web/.next/server/app/api/index/route.js.nft.json +1 -1
  182. package/.next/standalone/apps/web/.next/server/app/api/jobs/[id]/route.js.nft.json +1 -1
  183. package/.next/standalone/apps/web/.next/server/app/api/jobs/route.js.nft.json +1 -1
  184. package/.next/standalone/apps/web/.next/server/app/api/media/[id]/route.js.nft.json +1 -1
  185. package/.next/standalone/apps/web/.next/server/app/api/media/process/route.js.nft.json +1 -1
  186. package/.next/standalone/apps/web/.next/server/app/api/media/route.js.nft.json +1 -1
  187. package/.next/standalone/apps/web/.next/server/app/api/media/stream/route.js.nft.json +1 -1
  188. package/.next/standalone/apps/web/.next/server/app/api/search/bing/route.js.nft.json +1 -1
  189. package/.next/standalone/apps/web/.next/server/app/api/search/brave/route.js.nft.json +1 -1
  190. package/.next/standalone/apps/web/.next/server/app/api/search/exa/route.js.nft.json +1 -1
  191. package/.next/standalone/apps/web/.next/server/app/api/search/google/route.js.nft.json +1 -1
  192. package/.next/standalone/apps/web/.next/server/app/api/search/route.js.nft.json +1 -1
  193. package/.next/standalone/apps/web/.next/server/app/api/search/tavily/route.js.nft.json +1 -1
  194. package/.next/standalone/apps/web/.next/server/app/api/server/agent/route.js.nft.json +1 -1
  195. package/.next/standalone/apps/web/.next/server/app/api/server/download/route.js.nft.json +1 -1
  196. package/.next/standalone/apps/web/.next/server/app/api/server/generate/route.js.nft.json +1 -1
  197. package/.next/standalone/apps/web/.next/server/app/api/server/local/route.js.nft.json +1 -1
  198. package/.next/standalone/apps/web/.next/server/app/api/servers/route.js.nft.json +1 -1
  199. package/.next/standalone/apps/web/.next/server/app/api/tabular/query/route.js.nft.json +1 -1
  200. package/.next/standalone/apps/web/.next/server/app/api/tabular/route.js.nft.json +1 -1
  201. package/.next/standalone/apps/web/.next/server/app/api/upload-file/route.js.nft.json +1 -1
  202. package/.next/standalone/apps/web/.next/server/app/api/uploads/[...slug]/route.js.nft.json +1 -1
  203. package/.next/standalone/apps/web/.next/server/app/chat/page_client-reference-manifest.js +1 -1
  204. package/.next/standalone/apps/web/.next/server/app/chat.html +1 -1
  205. package/.next/standalone/apps/web/.next/server/app/chat.rsc +19 -19
  206. package/.next/standalone/apps/web/.next/server/app/chat.segments/_full.segment.rsc +19 -19
  207. package/.next/standalone/apps/web/.next/server/app/chat.segments/_head.segment.rsc +4 -4
  208. package/.next/standalone/apps/web/.next/server/app/chat.segments/_index.segment.rsc +12 -12
  209. package/.next/standalone/apps/web/.next/server/app/chat.segments/_tree.segment.rsc +2 -2
  210. package/.next/standalone/apps/web/.next/server/app/chat.segments/chat/__PAGE__.segment.rsc +3 -3
  211. package/.next/standalone/apps/web/.next/server/app/chat.segments/chat.segment.rsc +3 -3
  212. package/.next/standalone/apps/web/.next/server/app/data/page.js.nft.json +1 -1
  213. package/.next/standalone/apps/web/.next/server/app/data/page_client-reference-manifest.js +1 -1
  214. package/.next/standalone/apps/web/.next/server/app/data.html +1 -1
  215. package/.next/standalone/apps/web/.next/server/app/data.rsc +19 -19
  216. package/.next/standalone/apps/web/.next/server/app/data.segments/_full.segment.rsc +19 -19
  217. package/.next/standalone/apps/web/.next/server/app/data.segments/_head.segment.rsc +4 -4
  218. package/.next/standalone/apps/web/.next/server/app/data.segments/_index.segment.rsc +12 -12
  219. package/.next/standalone/apps/web/.next/server/app/data.segments/_tree.segment.rsc +2 -2
  220. package/.next/standalone/apps/web/.next/server/app/data.segments/data/__PAGE__.segment.rsc +3 -3
  221. package/.next/standalone/apps/web/.next/server/app/data.segments/data.segment.rsc +3 -3
  222. package/.next/standalone/apps/web/.next/server/app/index.html +1 -1
  223. package/.next/standalone/apps/web/.next/server/app/index.rsc +17 -17
  224. package/.next/standalone/apps/web/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  225. package/.next/standalone/apps/web/.next/server/app/index.segments/_full.segment.rsc +17 -17
  226. package/.next/standalone/apps/web/.next/server/app/index.segments/_head.segment.rsc +4 -4
  227. package/.next/standalone/apps/web/.next/server/app/index.segments/_index.segment.rsc +12 -12
  228. package/.next/standalone/apps/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  229. package/.next/standalone/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
  230. package/.next/standalone/apps/web/.next/server/app/settings/page.js.nft.json +1 -1
  231. package/.next/standalone/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  232. package/.next/standalone/apps/web/.next/server/app/settings.html +1 -1
  233. package/.next/standalone/apps/web/.next/server/app/settings.rsc +19 -19
  234. package/.next/standalone/apps/web/.next/server/app/settings.segments/_full.segment.rsc +19 -19
  235. package/.next/standalone/apps/web/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  236. package/.next/standalone/apps/web/.next/server/app/settings.segments/_index.segment.rsc +12 -12
  237. package/.next/standalone/apps/web/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  238. package/.next/standalone/apps/web/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  239. package/.next/standalone/apps/web/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  240. package/.next/standalone/apps/web/.next/server/chunks/0598_next_dist_esm_build_templates_app-route_0kbcuk-.js +9 -7
  241. package/.next/standalone/apps/web/.next/server/chunks/0598_next_dist_esm_build_templates_app-route_0sbf4ny.js +2 -2
  242. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__06gca-c._.js +1 -1
  243. package/.next/standalone/apps/web/.next/server/chunks/{[root-of-the-server]__1idgvjh._.js → [root-of-the-server]__0pmypq8._.js} +3 -3
  244. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0tlfusr._.js +1 -1
  245. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0x_a1jw._.js +1 -1
  246. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__1-f7u-_._.js +1 -1
  247. package/.next/{server/chunks/[root-of-the-server]__1lhls88._.js → standalone/apps/web/.next/server/chunks/[root-of-the-server]__13xnq6l._.js} +1 -1
  248. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__16jk_se._.js +1 -1
  249. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__1s_l1di._.js +1 -1
  250. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__1u4frv-._.js +1 -1
  251. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__1w00ho9._.js +1 -1
  252. package/.next/standalone/apps/web/.next/server/chunks/_1ncm_ks._.js +1 -1
  253. package/.next/standalone/apps/web/.next/server/chunks/_1sx94ue._.js +1 -1
  254. package/.next/standalone/apps/web/.next/server/chunks/_20il040._.js +1 -1
  255. package/.next/standalone/apps/web/.next/server/chunks/packages_0cc5bxf._.js +1 -1
  256. package/.next/standalone/apps/web/.next/server/chunks/packages_1ggqion._.js +1 -1
  257. package/.next/standalone/apps/web/.next/server/chunks/packages_core_src_embeddings_registry_ts_05dafmb._.js +1 -1
  258. package/.next/standalone/apps/web/.next/server/chunks/ssr/[root-of-the-server]__1k1mxo2._.js +1 -1
  259. package/.next/standalone/apps/web/.next/server/chunks/ssr/_0ffb2sn._.js +1 -1
  260. package/.next/standalone/apps/web/.next/server/chunks/ssr/_169pdlf._.js +2 -2
  261. package/.next/standalone/apps/web/.next/server/chunks/ssr/_18jagqc._.js +1 -1
  262. package/.next/standalone/apps/web/.next/server/chunks/ssr/_1b5ufkw._.js +3 -0
  263. package/.next/standalone/apps/web/.next/server/chunks/ssr/_1yqlx1m._.js +1 -1
  264. package/.next/standalone/apps/web/.next/server/chunks/ssr/packages_core_src_generator_generate-server_ts_15zzrn2._.js +1 -1
  265. package/.next/standalone/apps/web/.next/server/middleware-build-manifest.js +3 -3
  266. package/.next/standalone/apps/web/.next/server/pages/404.html +1 -1
  267. package/.next/standalone/apps/web/.next/server/pages/500.html +1 -1
  268. package/.next/standalone/apps/web/.next/static/chunks/1_knu6c2q82r_.js +1 -0
  269. package/.next/{static/chunks/37j20in7beze4.js → standalone/apps/web/.next/static/chunks/1gl22nlyonq0i.js} +1 -1
  270. package/.next/standalone/apps/web/.next/static/chunks/1xr94e4n5c_m_.css +1 -0
  271. package/.next/standalone/apps/web/.next/static/chunks/{2h-80z3n8mi3d.js → 2diknd81m_z55.js} +1 -1
  272. package/.next/standalone/apps/web/.next/static/chunks/2iog7bwy_5c7e.js +1 -0
  273. package/.next/standalone/apps/web/.next/static/chunks/{2dvtyg2k2op4x.js → 2q_4eq7-dkww2.js} +2 -2
  274. package/.next/standalone/apps/web/.next/static/chunks/{2x1t8sqst0ipu.js → 2t0il37i0nvoo.js} +1 -1
  275. package/.next/standalone/apps/web/.next/static/media/pdf.worker.min.43wid0v0nfx-f.mjs +29 -0
  276. package/.next/standalone/apps/web/CHANGELOG.md +12 -0
  277. package/.next/standalone/apps/web/app/api/chat/route.ts +22 -63
  278. package/.next/standalone/apps/web/app/api/chat/tools.ts +66 -11
  279. package/.next/standalone/apps/web/app/api/media/process/route.ts +88 -40
  280. package/.next/standalone/apps/web/components/chat/chat-workspace.tsx +10 -10
  281. package/.next/standalone/apps/web/components/chat/message-item.tsx +1 -8
  282. package/.next/standalone/apps/web/components/data/media-panel.tsx +1 -2
  283. package/.next/standalone/apps/web/components/data/upload-panel.tsx +7 -4
  284. package/.next/standalone/apps/web/lib/pdf-images.ts +30 -1
  285. package/.next/standalone/apps/web/lib/retrieval-routing.ts +72 -0
  286. package/.next/standalone/apps/web/package.json +1 -1
  287. package/.next/standalone/apps/web/server.js +1 -1
  288. package/.next/standalone/packages/tools/doc-editor/CHANGELOG.md +7 -0
  289. package/.next/standalone/packages/tools/doc-editor/package.json +1 -1
  290. package/.next/static/chunks/1_knu6c2q82r_.js +1 -0
  291. package/.next/{standalone/apps/web/.next/static/chunks/37j20in7beze4.js → static/chunks/1gl22nlyonq0i.js} +1 -1
  292. package/.next/static/chunks/1xr94e4n5c_m_.css +1 -0
  293. package/.next/static/chunks/{2h-80z3n8mi3d.js → 2diknd81m_z55.js} +1 -1
  294. package/.next/static/chunks/2iog7bwy_5c7e.js +1 -0
  295. package/.next/static/chunks/{2dvtyg2k2op4x.js → 2q_4eq7-dkww2.js} +2 -2
  296. package/.next/static/chunks/{2x1t8sqst0ipu.js → 2t0il37i0nvoo.js} +1 -1
  297. package/.next/static/media/pdf.worker.min.43wid0v0nfx-f.mjs +29 -0
  298. package/CHANGELOG.md +12 -0
  299. package/app/api/chat/route.ts +22 -63
  300. package/app/api/chat/tools.ts +66 -11
  301. package/app/api/media/process/route.ts +88 -40
  302. package/components/chat/chat-workspace.tsx +10 -10
  303. package/components/chat/message-item.tsx +1 -8
  304. package/components/data/media-panel.tsx +1 -2
  305. package/components/data/upload-panel.tsx +7 -4
  306. package/lib/pdf-images.ts +30 -1
  307. package/lib/retrieval-routing.ts +72 -0
  308. package/package.json +6 -6
  309. package/.next/server/chunks/ssr/apps_web_lib_pdf-images_ts_13-l5wa._.js +0 -3
  310. package/.next/standalone/apps/web/.next/server/chunks/ssr/apps_web_lib_pdf-images_ts_13-l5wa._.js +0 -3
  311. package/.next/standalone/apps/web/.next/static/chunks/2ib3e0xrrdait.js +0 -1
  312. package/.next/standalone/apps/web/.next/static/chunks/38f3p67a1uufx.js +0 -1
  313. package/.next/standalone/apps/web/.next/static/chunks/3fe06cefiv690.css +0 -1
  314. package/.next/static/chunks/2ib3e0xrrdait.js +0 -1
  315. package/.next/static/chunks/38f3p67a1uufx.js +0 -1
  316. package/.next/static/chunks/3fe06cefiv690.css +0 -1
  317. /package/.next/standalone/apps/web/.next/static/{whR5PvI0kapiCtaze5Lb0 → YT7FJOJe86DX-Sa61-JJz}/_buildManifest.js +0 -0
  318. /package/.next/standalone/apps/web/.next/static/{whR5PvI0kapiCtaze5Lb0 → YT7FJOJe86DX-Sa61-JJz}/_clientMiddlewareManifest.js +0 -0
  319. /package/.next/standalone/apps/web/.next/static/{whR5PvI0kapiCtaze5Lb0 → YT7FJOJe86DX-Sa61-JJz}/_ssgManifest.js +0 -0
  320. /package/.next/static/{whR5PvI0kapiCtaze5Lb0 → YT7FJOJe86DX-Sa61-JJz}/_buildManifest.js +0 -0
  321. /package/.next/static/{whR5PvI0kapiCtaze5Lb0 → YT7FJOJe86DX-Sa61-JJz}/_clientMiddlewareManifest.js +0 -0
  322. /package/.next/static/{whR5PvI0kapiCtaze5Lb0 → YT7FJOJe86DX-Sa61-JJz}/_ssgManifest.js +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # larkup
2
2
 
3
+ ## 0.1.42
4
+
5
+ ### Patch Changes
6
+
7
+ - Improve smart RAG and web fallback, PDF visual retrieval, and resilient video indexing.
8
+ - Updated dependencies
9
+ - @larkup/core@0.2.6
10
+ - @larkup/marketplace@0.1.12
11
+ - @larkup/scraper@0.1.30
12
+ - @larkup/tool-doc-editor@0.2.7
13
+ - @larkup/tool-video-audio@0.2.4
14
+
3
15
  ## 0.1.41
4
16
 
5
17
  ### Patch Changes
@@ -16,6 +16,11 @@ import type { CustomModelConfig } from '@larkup/core/types';
16
16
  import { DEFAULT_SYSTEM_PROMPT } from '@larkup/core/types';
17
17
 
18
18
  import { getChatTools } from './tools';
19
+ import {
20
+ requiresCurrentWebSearch,
21
+ requiresKnowledgeBaseSearch,
22
+ retrievalToolsForStep,
23
+ } from '@/lib/retrieval-routing';
19
24
 
20
25
  export const maxDuration = 60;
21
26
 
@@ -66,7 +71,7 @@ const SMART_RETRIEVAL_POLICY = `
66
71
 
67
72
  TOOL RULES:
68
73
  1. searchKnowledgeBase → Use FIRST when the question is about the user's indexed data (documents, files, images, videos, diagrams, anything they uploaded).
69
- 2. webSearch → Use ONLY after searchKnowledgeBase returned nothing relevant AND the question needs current/public facts.
74
+ 2. webSearch → Use for clearly current/public facts. If a prior local search was not relevant or failed, use it as the one permitted fallback. If web search fails, use the knowledge base as the one permitted fallback.
70
75
  3. Other tools → Use when the question specifically requires them.
71
76
  4. General knowledge → Answer directly for common knowledge unrelated to the user's data.
72
77
  5. If nothing helps, say so honestly.
@@ -75,6 +80,8 @@ DO:
75
80
  - Keep answers short and direct. 1–3 sentences unless the task is complex.
76
81
  - Search at most ONCE per tool per turn. Never repeat the same search.
77
82
  - For questions about the user's own facts or preferences (for example, "what is my favourite fruit?"), searchKnowledgeBase is required before answering.
83
+ - If a knowledge-base result contains PDF images and the question asks what is shown, named, counted, or connected in that visual, call analyzeImageDeeply before answering. Call presentMedia when the user asks to see the image.
84
+ - For an indexed video outcome question, inspect endingContext before saying that a winner or final score is unavailable. Use only explicitly supported evidence.
78
85
  - Use presentMedia to show images/videos/audio from indexed content.
79
86
  - For follow-ups like "show me that", reuse the mediaAssetId from earlier results — do not search again.
80
87
 
@@ -103,37 +110,6 @@ function latestUserText(messages: UIMessage[]): string {
103
110
  return '';
104
111
  }
105
112
 
106
- /** Only force KB search when the user explicitly refers to their own data.
107
- * Matches "my document", "our files", "the uploaded image" etc. but NOT
108
- * generic uses like "I want to know", "can you tell me", "I think". */
109
- function requiresKnowledgeBaseSearch(text: string): boolean {
110
- // Skip generic conversational patterns
111
- if (/^(hi|hello|hey|thanks|thank you|ok|sure|yes|no|please|help)\b/i.test(text.trim())) {
112
- return false;
113
- }
114
- // Match possessive references to user data
115
- return (
116
- /\b(my|our)\s+(favo(?:u)?rite|preference|choice|answer|name|result|score|winner)\b/i.test(
117
- text,
118
- ) ||
119
- /\b(?:what|which|who|where|when|how)\b[\s\S]{0,80}\b(my|our)\b/i.test(text) ||
120
- /\b(my|our)\s+(document|file|data|image|picture|diagram|video|audio|upload|corpus|knowledge|database|db|pdf|report|spreadsheet|presentation)/i.test(
121
- text,
122
- ) ||
123
- /\b(the|this|that)\s+(document|file|diagram|image|picture|upload|pdf|report)/i.test(text) ||
124
- /\b(uploaded?|indexed|scraped|knowledge base|corpus)\b/i.test(text) ||
125
- /\bshow\s+me\b/i.test(text)
126
- );
127
- }
128
-
129
- /** Questions whose answer is inherently time-sensitive should not rely on the
130
- * model's training snapshot when Web Search is available. */
131
- function requiresCurrentWebSearch(text: string): boolean {
132
- return /\b(who won|winner|final score|match result|latest|current|today|yesterday|breaking news|weather|stock price|exchange rate|election result|schedule)\b/i.test(
133
- text,
134
- );
135
- }
136
-
137
113
  export async function POST(req: Request) {
138
114
  const {
139
115
  messages,
@@ -401,7 +377,12 @@ ${fieldLines}`;
401
377
  : '') +
402
378
  tabularContext +
403
379
  docContext;
404
- const tools = await getChatTools({ serverId, docSessionId, config });
380
+ const tools = await getChatTools({
381
+ serverId,
382
+ docSessionId,
383
+ config,
384
+ origin: new URL(req.url).origin,
385
+ });
405
386
  const userText = latestUserText(messagesToProcess);
406
387
  const forceKnowledgeBaseSearch =
407
388
  requiresKnowledgeBaseSearch(userText) && Boolean(tools.searchKnowledgeBase);
@@ -420,38 +401,16 @@ ${fieldLines}`;
420
401
  system: systemPrompt,
421
402
  messages: await convertToModelMessages(safeMessages, { tools }),
422
403
  maxOutputTokens: 4096,
423
- // Three steps: retrieve → optionally present media → answer.
404
+ // Three steps: retrieve → optionally inspect/present media → answer.
424
405
  stopWhen: stepCountIs(3),
425
406
  toolChoice: 'auto',
426
- prepareStep: ({ stepNumber }) => {
427
- // Step 0: force KB search if needed
428
- if (forceKnowledgeBaseSearch && stepNumber === 0) {
429
- return { toolChoice: { type: 'tool', toolName: 'searchKnowledgeBase' } };
430
- }
431
- // For questions about the user's data, let the model evaluate the KB
432
- // result before falling back to the web. Forcing both tools used to make
433
- // enabled web search override a perfectly relevant local answer.
434
- if (forceWebSearch && !forceKnowledgeBaseSearch && stepNumber === 0) {
435
- return { toolChoice: { type: 'tool', toolName: 'webSearch' } };
436
- }
437
- // After a forced tool, and after the optional KB → web fallback, remove
438
- // search tools. This guarantees one concise search surface rather than
439
- // a chain of near-identical web queries.
440
- if (stepNumber >= 2) {
441
- return {
442
- activeTools: Object.keys(tools).filter(
443
- (name) => name !== 'webSearch' && name !== 'searchKnowledgeBase',
444
- ),
445
- };
446
- }
447
- if (forceKnowledgeBaseSearch && stepNumber >= 1) {
448
- return { activeTools: Object.keys(tools).filter((name) => name !== 'searchKnowledgeBase') };
449
- }
450
- if (forceWebSearch && stepNumber >= 1) {
451
- return { activeTools: Object.keys(tools).filter((name) => name !== 'webSearch') };
452
- }
453
- return undefined;
454
- },
407
+ prepareStep: ({ stepNumber }) =>
408
+ retrievalToolsForStep({
409
+ stepNumber,
410
+ forceKnowledgeBaseSearch,
411
+ forceWebSearch: forceWebSearch && !forceKnowledgeBaseSearch,
412
+ toolNames: Object.keys(tools),
413
+ }),
455
414
  onFinish: async ({ usage, response }) => {
456
415
  const { trackUsageEvent, estimateCost } = await import('@larkup/core/analytics-store');
457
416
  const u = usage as any;
@@ -166,15 +166,21 @@ async function formatKnowledgeHits(query: string, rawHits: any[], topK: number)
166
166
  const trailingDocuments = timestampedDocuments.filter(
167
167
  (document) => Number(document.metadata?.endSecs ?? 0) >= mediaDuration - trailingHorizonSecs,
168
168
  );
169
+ // Result announcements can occur shortly before the literal final minutes
170
+ // (for example during a trophy or score recap). Preserve the latest explicit
171
+ // outcome evidence from the whole indexed timeline, not only the trailing
172
+ // window returned by a semantic match.
173
+ const timelineOutcomeDocuments = isOutcomeQuery(query)
174
+ ? timestampedDocuments.filter((document) => isOutcomeEvidence(document.content)).slice(-3)
175
+ : [];
169
176
  const signaledEndingDocuments = isOutcomeQuery(query)
170
177
  ? trailingDocuments.filter((document) => isOutcomeEvidence(document.content)).slice(-2)
171
178
  : [];
172
179
  const timestampedEnding = [
173
180
  ...new Map(
174
- [...signaledEndingDocuments, ...trailingDocuments.slice(-4)].map((document) => [
175
- document.id,
176
- document,
177
- ]),
181
+ [...timelineOutcomeDocuments, ...signaledEndingDocuments, ...trailingDocuments.slice(-4)].map(
182
+ (document) => [document.id, document],
183
+ ),
178
184
  ).values(),
179
185
  ].sort(
180
186
  (left, right) => Number(left.metadata?.endSecs ?? 0) - Number(right.metadata?.endSecs ?? 0),
@@ -287,7 +293,18 @@ async function formatKnowledgeHits(query: string, rawHits: any[], topK: number)
287
293
  // document, rather than creating media-library assets. Expose those
288
294
  // URLs explicitly so the presentation tool can validate and render
289
295
  // them in chat.
290
- images: hit.metadata?.images,
296
+ images:
297
+ hit.metadata?.images ??
298
+ (hit.metadata?.isImage && hit.metadata?.imageUrl
299
+ ? [
300
+ {
301
+ imageUrl: hit.metadata.imageUrl,
302
+ pageNumber: hit.metadata.pageNumber,
303
+ index: hit.metadata.index,
304
+ description: hit.metadata.description,
305
+ },
306
+ ]
307
+ : undefined),
291
308
  metadata: hit.metadata,
292
309
  timelineContext,
293
310
  endingContext: hitIndex === 0 ? endingContext : undefined,
@@ -312,13 +329,15 @@ export async function getChatTools(context: {
312
329
  serverId?: string;
313
330
  docSessionId?: string;
314
331
  config?: any;
332
+ /** Public origin of the incoming chat request, used for local media URLs. */
333
+ origin?: string;
315
334
  }) {
316
335
  const { serverId, docSessionId, config } = context;
317
336
 
318
337
  const builtInTools: Record<string, any> = {
319
338
  searchKnowledgeBase: tool({
320
339
  description:
321
- "HIGHEST PRIORITY TOOL — Search the user's private RAG knowledge base. ALWAYS use this FIRST before webSearch or any other tool when the question may relate to the user's indexed data. This includes questions about documents, files, images, diagrams, videos, or anything the user may have uploaded. Do not use for general knowledge, casual conversation, or writing tasks. Search once with a focused query. For media, inspect the returned timeline and endingContext, and reuse exact mediaAssetId/startSecs/endSecs values with presentMedia when a preview is useful. Never guess timestamps.",
340
+ "HIGHEST PRIORITY TOOL — Search the user's private RAG knowledge base. ALWAYS use this FIRST before webSearch or any other tool when the question may relate to the user's indexed data. This includes questions about documents, files, images, diagrams, videos, or anything the user may have uploaded. Do not use for general knowledge, casual conversation, or writing tasks. Search once with a focused query. If it returns no relevant evidence or an error and the question asks for public/current information, webSearch is available once as a fallback. For media, inspect the returned timeline and endingContext, and reuse exact mediaAssetId/startSecs/endSecs values with presentMedia when a preview is useful. Never guess timestamps.",
322
341
  inputSchema: z.object({
323
342
  query: z.string().describe('The search query for the knowledge base.'),
324
343
  }),
@@ -620,7 +639,10 @@ export async function getChatTools(context: {
620
639
  // Fetch the image to base64
621
640
  // Handle both absolute URLs and relative local uploads
622
641
  const fetchUrl = imageUrl.startsWith('/')
623
- ? `http://127.0.0.1:${process.env.PORT || 3000}${imageUrl}`
642
+ ? new URL(
643
+ imageUrl,
644
+ context.origin ?? `http://127.0.0.1:${process.env.PORT || 3000}`,
645
+ ).toString()
624
646
  : imageUrl;
625
647
  const res = await fetch(fetchUrl);
626
648
  if (!res.ok) throw new Error(`Failed to fetch image: ${res.statusText}`);
@@ -631,7 +653,10 @@ export async function getChatTools(context: {
631
653
 
632
654
  // Post to our describe-image endpoint which handles model setup
633
655
  const descRes = await fetch(
634
- `http://127.0.0.1:${process.env.PORT || 3000}/api/describe-image`,
656
+ new URL(
657
+ '/api/describe-image',
658
+ context.origin ?? `http://127.0.0.1:${process.env.PORT || 3000}`,
659
+ ).toString(),
635
660
  {
636
661
  method: 'POST',
637
662
  headers: { 'Content-Type': 'application/json' },
@@ -936,11 +961,35 @@ export async function getChatTools(context: {
936
961
 
937
962
  webSearch: tool({
938
963
  description:
939
- "Search the public internet for current information, news, or facts. IMPORTANT: Only use this AFTER searchKnowledgeBase has been tried first and returned no relevant results. Never use webSearch as a first resort the user's indexed knowledge base always takes priority. Use webSearch for questions about current events, public facts, or topics clearly outside the user's indexed data.",
964
+ 'Search the public internet for current information, news, or facts. Use it first for clearly current/public requests, or once after an irrelevant local search. If it fails or returns no useful results, searchKnowledgeBase is available once as a fallback. Never repeat either search tool in the same turn.',
940
965
  inputSchema: z.object({
941
966
  query: z.string().describe('The search query.'),
942
967
  }),
943
968
  execute: async ({ query }) => {
969
+ const knowledgeBaseFallback = async (error: string) => {
970
+ try {
971
+ const knowledge = await queryKnowledgeBase(query, 5, serverId ?? null);
972
+ return {
973
+ error,
974
+ results: [],
975
+ fallback: {
976
+ source: 'knowledge-base',
977
+ attemptedBecause: 'Public web search failed or returned no results.',
978
+ ...knowledge,
979
+ },
980
+ };
981
+ } catch (fallbackError: any) {
982
+ return {
983
+ error,
984
+ results: [],
985
+ fallback: {
986
+ source: 'knowledge-base',
987
+ error: fallbackError?.message || 'Knowledge-base fallback failed.',
988
+ hits: [],
989
+ },
990
+ };
991
+ }
992
+ };
944
993
  const provider = config?.webSearchProvider || 'tavily';
945
994
  const req = new Request('http://localhost/api/search', {
946
995
  method: 'POST',
@@ -980,11 +1029,14 @@ export async function getChatTools(context: {
980
1029
 
981
1030
  if (res.status >= 400) {
982
1031
  const err = await res.json();
983
- return { error: err.error || 'Search failed', results: [] };
1032
+ return knowledgeBaseFallback(err.error || 'Search failed');
984
1033
  }
985
1034
 
986
1035
  const data = await res.json();
987
1036
  const items = data.items || data.results || [];
1037
+ if (items.length === 0) {
1038
+ return knowledgeBaseFallback('Public search returned no results.');
1039
+ }
988
1040
  return {
989
1041
  results: items.slice(0, 10).map((i: any) => ({
990
1042
  title: i.title,
@@ -993,7 +1045,7 @@ export async function getChatTools(context: {
993
1045
  })),
994
1046
  };
995
1047
  } catch (e: any) {
996
- return { error: e.message || 'Search failed', results: [] };
1048
+ return knowledgeBaseFallback(e.message || 'Search failed');
997
1049
  }
998
1050
  },
999
1051
  }),
@@ -1016,6 +1068,9 @@ export async function getChatTools(context: {
1016
1068
  // Keeping it available prevents an enabled web-search configuration
1017
1069
  // from silently removing the user's local knowledge base.
1018
1070
  'searchKnowledgeBase',
1071
+ // PDF/image hits can require one visual pass after retrieval. This is
1072
+ // available only to a follow-up tool step, never auto-run on chat.
1073
+ 'analyzeImageDeeply',
1019
1074
  'presentMedia',
1020
1075
  'fillDocumentForm',
1021
1076
  'editDocument',
@@ -646,7 +646,9 @@ async function processMediaWithTool(
646
646
  const scenePromise = (async () => {
647
647
  const extractedFrames = result.frames as { path: string; timestampSecs: number }[];
648
648
  const analysisWindowSecs = visualAnalysisWindow(result.meta.durationSecs);
649
- const frameGroups = groupFramesByWindow(extractedFrames, analysisWindowSecs, 6);
649
+ // Chronological batches preserve visual context while keeping a
650
+ // long recording within a practical vision-request budget.
651
+ const frameGroups = groupFramesByWindow(extractedFrames, analysisWindowSecs, 12);
650
652
  if (frameGroups.length === 0) {
651
653
  await reportStage('vision', {
652
654
  status: 'skipped',
@@ -662,10 +664,37 @@ async function processMediaWithTool(
662
664
  message: `Understanding 0 of ${frameGroups.length} visual sequences...`,
663
665
  });
664
666
  let analyzed = 0;
667
+ let skipped = 0;
668
+ let visionThrottled = false;
669
+ let lastVisionRequestAt = 0;
670
+ const visionProvider = globalConfig.chatProvider || globalConfig.embeddingProvider;
671
+ // Providers commonly rate-limit visual generation much more tightly
672
+ // than text generation. Serializing and pacing Google requests avoids
673
+ // turning a temporary 429 into a failed video index.
674
+ const visionConcurrency = visionProvider === 'google' ? 1 : limits.apiConcurrency;
675
+ const visionMinIntervalMs = visionProvider === 'google' ? 4_250 : 0;
676
+ const recordProgress = () => {
677
+ analyzed++;
678
+ const progressStep = Math.max(1, Math.ceil(frameGroups.length / 100));
679
+ if (analyzed % progressStep === 0 || analyzed === frameGroups.length) {
680
+ void reportStage('vision', {
681
+ status: 'running',
682
+ current: analyzed,
683
+ total: frameGroups.length,
684
+ unit: 'sequences',
685
+ message: `Understanding visual sequence ${analyzed} of ${frameGroups.length}...`,
686
+ }).catch(() => {});
687
+ }
688
+ };
665
689
  const descriptions = await mapConcurrent(
666
690
  frameGroups,
667
- limits.apiConcurrency,
691
+ visionConcurrency,
668
692
  async (frames: { path: string; timestampSecs: number }[]) => {
693
+ if (visionThrottled) {
694
+ skipped++;
695
+ recordProgress();
696
+ return null;
697
+ }
669
698
  const base64Images = await Promise.all(
670
699
  frames.map(async (frame) => (await fs.readFile(frame.path)).toString('base64')),
671
700
  );
@@ -675,14 +704,20 @@ async function processMediaWithTool(
675
704
  Math.max(startSecs + 1, frames.at(-1)!.timestampSecs + frameIntervalSecs),
676
705
  );
677
706
  const timestamps = frames.map((frame) => formatTime(frame.timestampSecs)).join(', ');
678
- const descRes = await fetchWithRetry(
679
- scopedApiUrl('/api/describe-image', reqUrl, serverId),
680
- {
681
- method: 'POST',
682
- headers: { 'Content-Type': 'application/json' },
683
- body: JSON.stringify({
684
- base64Images,
685
- prompt: `These are chronological frames from "${asset.fileName}" at ${timestamps}.
707
+ if (visionMinIntervalMs > 0) {
708
+ const remaining = visionMinIntervalMs - (Date.now() - lastVisionRequestAt);
709
+ if (remaining > 0) await new Promise((resolve) => setTimeout(resolve, remaining));
710
+ }
711
+ lastVisionRequestAt = Date.now();
712
+ try {
713
+ const descRes = await fetchWithRetry(
714
+ scopedApiUrl('/api/describe-image', reqUrl, serverId),
715
+ {
716
+ method: 'POST',
717
+ headers: { 'Content-Type': 'application/json' },
718
+ body: JSON.stringify({
719
+ base64Images,
720
+ prompt: `These are chronological frames from "${asset.fileName}" at ${timestamps}.
686
721
  Write one evidence note for this time range. Track people, actions, interactions, state changes,
687
722
  important objects, and exact meaningful on-screen text. Perform careful OCR, including Arabic and
688
723
  other right-to-left text without reversing labels. For scoreboards or result animations, compare
@@ -690,38 +725,44 @@ frames in timestamp order and report the latest visible score as the current/fin
690
725
  each number with the correct nearby team/person label. Record a winner or result only when visually
691
726
  supported. Omit boilerplate about facts that are not shown. Be factual, information-dense, and
692
727
  concise.`,
693
- }),
694
- },
695
- );
696
- analyzed++;
697
- const progressStep = Math.max(1, Math.ceil(frameGroups.length / 100));
698
- if (analyzed % progressStep === 0 || analyzed === frameGroups.length) {
699
- void reportStage('vision', {
700
- status: 'running',
701
- current: analyzed,
702
- total: frameGroups.length,
703
- unit: 'sequences',
704
- message: `Understanding visual sequence ${analyzed} of ${frameGroups.length}...`,
705
- }).catch(() => {});
706
- }
707
- if (!descRes.ok) {
708
- let detail = '';
709
- try {
710
- const errorBody = (await descRes.json()) as { error?: string };
711
- detail = errorBody.error?.trim() || '';
712
- } catch {
713
- // Keep the status-only fallback for non-JSON failures.
728
+ }),
729
+ },
730
+ );
731
+ if (!descRes.ok) {
732
+ let detail = '';
733
+ try {
734
+ const errorBody = (await descRes.json()) as { error?: string };
735
+ detail = errorBody.error?.trim() || '';
736
+ } catch {
737
+ // Keep the status-only fallback for non-JSON failures.
738
+ }
739
+ if (/quota|rate limit|resource_exhausted|too many requests/i.test(detail)) {
740
+ visionThrottled = true;
741
+ }
742
+ skipped++;
743
+ console.warn(
744
+ `Visual sequence ${formatTime(startSecs)}–${formatTime(endSecs)} was skipped: ${
745
+ detail || `HTTP ${descRes.status}`
746
+ }`,
747
+ );
748
+ return null;
714
749
  }
715
- throw new Error(
716
- `Visual sequence analysis failed (${descRes.status})${
717
- detail ? `: ${detail}` : '.'
718
- }`,
750
+ const { description } = (await descRes.json()) as { description?: string };
751
+ return description
752
+ ? { text: description, startSecs, endSecs: Math.max(startSecs + 1, endSecs) }
753
+ : null;
754
+ } catch (error) {
755
+ skipped++;
756
+ console.warn(
757
+ `Visual sequence ${formatTime(startSecs)}–${formatTime(
758
+ endSecs,
759
+ )} failed; preserving the remaining transcript evidence.`,
760
+ error,
719
761
  );
762
+ return null;
763
+ } finally {
764
+ recordProgress();
720
765
  }
721
- const { description } = (await descRes.json()) as { description?: string };
722
- return description
723
- ? { text: description, startSecs, endSecs: Math.max(startSecs + 1, endSecs) }
724
- : null;
725
766
  },
726
767
  );
727
768
  await reportStage('vision', {
@@ -729,7 +770,12 @@ concise.`,
729
770
  current: frameGroups.length,
730
771
  total: frameGroups.length,
731
772
  unit: 'sequences',
732
- message: `Understood ${frameGroups.length} visual sequences with OCR.`,
773
+ message:
774
+ skipped > 0
775
+ ? `Understood ${
776
+ frameGroups.length - skipped
777
+ } visual sequences; ${skipped} were skipped while the vision provider was unavailable. Speech evidence remains searchable.`
778
+ : `Understood ${frameGroups.length} visual sequences with OCR.`,
733
779
  });
734
780
  return descriptions;
735
781
  })();
@@ -1002,6 +1048,8 @@ concise.`,
1002
1048
  function visualAnalysisWindow(durationSecs: number): number {
1003
1049
  if (durationSecs >= 4 * 60 * 60) return 15 * 60;
1004
1050
  if (durationSecs > 60 * 60) return 5 * 60;
1051
+ if (durationSecs > 30 * 60) return 3 * 60;
1052
+ if (durationSecs > 10 * 60) return 2 * 60;
1005
1053
  return 60;
1006
1054
  }
1007
1055
 
@@ -107,7 +107,7 @@ function AttachmentCard({
107
107
  <div className="size-7 shrink-0 rounded-md overflow-hidden bg-background border border-border/90 flex items-center justify-center p-1">
108
108
  <img src={iconPath} alt={file.name} className="w-full h-full object-contain" />
109
109
  </div>
110
- <div className="flex flex-col justify-center max-w-[120px]">
110
+ <div className="flex flex-col justify-center max-w-30">
111
111
  <span className="text-xs font-medium truncate text-foreground">{file.name}</span>
112
112
  <span className="text-[10px] text-muted-foreground">
113
113
  {(file.size / 1024).toFixed(0)} KB
@@ -684,7 +684,7 @@ function ChatWorkspaceInner() {
684
684
  <button
685
685
  type="button"
686
686
  onClick={() => setShowModelSelect((o) => !o)}
687
- className="flex w-[300px] justify-between items-center gap-1.5 rounded-lg border border-border bg-white dark:bg-card px-3 py-1.5 text-xs text-foreground transition hover:bg-muted"
687
+ className="flex w-75 justify-between items-center gap-1.5 rounded-lg border border-border bg-white dark:bg-card px-3 py-1.5 text-xs text-foreground transition hover:bg-muted"
688
688
  >
689
689
  <span className="flex items-center gap-2 truncate">
690
690
  {selectedModelData && (
@@ -709,7 +709,7 @@ function ChatWorkspaceInner() {
709
709
  {showModelSelect && (
710
710
  <>
711
711
  <div className="fixed inset-0 z-40" onClick={() => setShowModelSelect(false)} />
712
- <div className="absolute right-0 top-full z-50 mt-1 w-[300px] rounded-lg border border-border bg-card ">
712
+ <div className="absolute right-0 top-full z-50 mt-1 w-75 rounded-lg border border-border bg-card ">
713
713
  {/* Search input */}
714
714
  <div className="flex items-center gap-2 border-b border-border px-3 py-2">
715
715
  <Search className="size-3.5 shrink-0 text-muted-foreground" />
@@ -815,7 +815,7 @@ function ChatWorkspaceInner() {
815
815
  type="button"
816
816
  className="flex h-8 w-8 items-center justify-center rounded-lg text-muted-foreground transition hover:bg-secondary hover:text-foreground"
817
817
  >
818
- <History className="h-[16px] w-[16px]" />
818
+ <History className="h-4 w-4" />
819
819
  </button>
820
820
  }
821
821
  />
@@ -823,7 +823,7 @@ function ChatWorkspaceInner() {
823
823
  />
824
824
  <TooltipContent>Chat History</TooltipContent>
825
825
  </Tooltip>
826
- <SheetContent side="left" className="w-[300px] sm:w-[350px] p-0 flex flex-col">
826
+ <SheetContent side="left" className="w-75 sm:w-87.5 p-0 flex flex-col">
827
827
  <SheetHeader className="p-4 border-b">
828
828
  <SheetTitle className="text-sm font-semibold">Chat History</SheetTitle>
829
829
  </SheetHeader>
@@ -890,7 +890,7 @@ function ChatWorkspaceInner() {
890
890
  onClick={newChat}
891
891
  className="flex h-8 w-8 items-center justify-center rounded-lg text-muted-foreground transition hover:bg-secondary hover:text-foreground"
892
892
  >
893
- <SquarePen className="h-[16px] w-[16px]" />
893
+ <SquarePen className="h-4 w-4" />
894
894
  </button>
895
895
  }
896
896
  />
@@ -1016,7 +1016,7 @@ function ChatWorkspaceInner() {
1016
1016
  type="button"
1017
1017
  className="flex h-9 w-9 shrink-0 items-center justify-center rounded-xl text-muted-foreground transition hover:bg-secondary hover:text-foreground"
1018
1018
  >
1019
- <Plus className="h-[18px] w-[18px]" />
1019
+ <Plus className="h-4.5 w-4.5" />
1020
1020
  </button>
1021
1021
  }
1022
1022
  />
@@ -1143,7 +1143,7 @@ function ChatWorkspaceInner() {
1143
1143
  rows={1}
1144
1144
  disabled={!ready}
1145
1145
  placeholder="How can I help you…"
1146
- className="max-h-48 min-h-[44px] w-full resize-none bg-transparent px-2 py-3 text-[15px] leading-relaxed text-foreground outline-none placeholder:text-muted-foreground [&::-webkit-scrollbar]:hidden disabled:opacity-50"
1146
+ className="max-h-48 min-h-11 w-full resize-none bg-transparent px-2 py-3 text-[15px] leading-relaxed text-foreground outline-none placeholder:text-muted-foreground [&::-webkit-scrollbar]:hidden disabled:opacity-50"
1147
1147
  />
1148
1148
  </div>
1149
1149
 
@@ -1153,9 +1153,9 @@ function ChatWorkspaceInner() {
1153
1153
  className="flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-primary text-primary-foreground transition hover:opacity-90 disabled:opacity-40"
1154
1154
  >
1155
1155
  {isBusy || isDocLoading ? (
1156
- <Loader2 className="h-[18px] w-[18px] animate-spin" />
1156
+ <Loader2 className="h-4.5 w-4.5 animate-spin" />
1157
1157
  ) : (
1158
- <ArrowUp className="h-[18px] w-[18px]" />
1158
+ <ArrowUp className="h-4.5 w-4.5" />
1159
1159
  )}
1160
1160
  </button>
1161
1161
  </div>
@@ -538,13 +538,9 @@ export function MessageItem({
538
538
  const editor = useDocEditor();
539
539
  updateFromToolResult = editor.updateFromToolResult;
540
540
  openCanvas = editor.openCanvas;
541
- } catch {
542
- // Not wrapped in DocEditorProvider in some contexts
543
- }
541
+ } catch {}
544
542
 
545
543
  const anyMessage = message as any;
546
-
547
- // Stabilize the parts array to prevent re-renders from creating new array refs each time
548
544
  const parts: any[] = useMemo(() => {
549
545
  const p: any[] = message.parts ? [...message.parts] : [];
550
546
  if (anyMessage.toolInvocations && Array.isArray(anyMessage.toolInvocations)) {
@@ -563,11 +559,8 @@ export function MessageItem({
563
559
  return p;
564
560
  }, [message.parts, anyMessage.toolInvocations]);
565
561
 
566
- // Track which tool call IDs have already been applied to prevent re-application
567
562
  const appliedToolCallsRef = useRef<Set<string>>(new Set());
568
563
 
569
- // Effect to apply document edits to the Canvas context
570
- // Uses a stable fingerprint to avoid re-triggering on every render
571
564
  const docToolFingerprint = useMemo(() => {
572
565
  if (!isLast) return '';
573
566
  return parts
@@ -731,8 +731,7 @@ function MediaContent({
731
731
  if (!res.ok) throw new Error(data.error ?? 'Media import failed');
732
732
  const ids = (data.assets as MediaAsset[]).map((asset) => asset.id);
733
733
  toast.success(`${mediaType === 'video' ? 'Video' : 'Audio'} import started`, {
734
- description:
735
- 'Queued for download and indexing. Live progress will appear once the source starts responding; you can safely leave in the meantime.',
734
+ description: 'Queued for download and indexing. You can safely leave in the meantime.',
736
735
  duration: 8_000,
737
736
  });
738
737
  await processAssets(ids);
@@ -219,7 +219,10 @@ export function UploadPanel({ onAdded }: { onAdded: () => void }) {
219
219
  format: 'plain',
220
220
  rawContent: text,
221
221
  fileObject: file,
222
- indexImages: !text || text.trim().length === 0,
222
+ // PDF diagrams are often embedded alongside ordinary text. Keep
223
+ // visual indexing on by default, with the existing per-file toggle
224
+ // available for users who only want text extraction.
225
+ indexImages: true,
223
226
  });
224
227
  } else {
225
228
  const content = await file.text();
@@ -572,7 +575,7 @@ export function UploadPanel({ onAdded }: { onAdded: () => void }) {
572
575
  </button>
573
576
  </div>
574
577
  </div>
575
- <ul className="space-y-1.5 max-h-[350px] overflow-y-auto pr-1">
578
+ <ul className="space-y-1.5 max-h-87.5 overflow-y-auto pr-1">
576
579
  {staged.map((f) => (
577
580
  <li
578
581
  key={f.id}
@@ -782,7 +785,7 @@ export function UploadPanel({ onAdded }: { onAdded: () => void }) {
782
785
  <p className="text-[11px] text-muted-foreground mb-2 -mt-1">
783
786
  Selected columns will be combined to form the searchable content.
784
787
  </p>
785
- <div className="max-h-[120px] overflow-y-auto space-y-2 pr-2">
788
+ <div className="max-h-30 overflow-y-auto space-y-2 pr-2">
786
789
  {editingFile.keys?.map((k) => {
787
790
  const isSelected = editingFile.contentKeys?.includes(k);
788
791
  return (
@@ -871,7 +874,7 @@ export function UploadPanel({ onAdded }: { onAdded: () => void }) {
871
874
  <p className="text-[11px] text-muted-foreground mb-2 -mt-1">
872
875
  These columns will be stored as searchable metadata.
873
876
  </p>
874
- <div className="max-h-[120px] overflow-y-auto space-y-2 pr-2">
877
+ <div className="max-h-30 overflow-y-auto space-y-2 pr-2">
875
878
  {editingFile.keys?.map((k) => {
876
879
  const isSelected = editingFile.metadataKeys?.includes(k);
877
880
  const disabled = k === editingFile.titleKey;