@circuitwall/jarela 2.3.0 → 2.5.0

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 (290) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-path-routes-manifest.json +5 -5
  3. package/.next/standalone/.next/build-manifest.json +2 -2
  4. package/.next/standalone/.next/prerender-manifest.json +4 -4
  5. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  6. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  7. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  11. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  12. package/.next/standalone/.next/server/app/_not-found.html +14 -2
  13. package/.next/standalone/.next/server/app/_not-found.rsc +16 -4
  14. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +16 -4
  15. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +16 -4
  16. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  17. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  18. package/.next/standalone/.next/server/app/api/v1/access/[identity]/route.js +7 -0
  19. package/.next/standalone/.next/server/app/api/v1/access/route.js +7 -0
  20. package/.next/standalone/.next/server/app/api/v1/agents/[id]/compact/route.js +7 -0
  21. package/.next/standalone/.next/server/app/api/v1/agents/[id]/display-filters/route.js +17 -5
  22. package/.next/standalone/.next/server/app/api/v1/agents/[id]/route.js +7 -0
  23. package/.next/standalone/.next/server/app/api/v1/agents/[id]/thread/route.js +17 -5
  24. package/.next/standalone/.next/server/app/api/v1/agents/route.js +7 -0
  25. package/.next/standalone/.next/server/app/api/v1/allowed-sites/[hostname]/route.js +7 -0
  26. package/.next/standalone/.next/server/app/api/v1/allowed-sites/route.js +7 -0
  27. package/.next/standalone/.next/server/app/api/v1/artifacts/lifecycle/route.js +7 -0
  28. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/chats/route.js +26 -32
  29. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/chats/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/ignores/[remote_jid]/route.js +18 -7
  31. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/ignores/route.js +18 -7
  32. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/lookup/route.js +26 -32
  33. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/lookup/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/pair/route.js +27 -33
  35. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/pair/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/route.js +26 -32
  37. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/route.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/routes/[route_id]/route.js +28 -12
  39. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/routes/route.js +28 -12
  40. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/status/route.js +26 -32
  41. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/status/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/v1/bridges/kinds/route.js +9 -2
  43. package/.next/standalone/.next/server/app/api/v1/bridges/kinds/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/api/v1/bridges/route.js +19 -7
  45. package/.next/standalone/.next/server/app/api/v1/bridges/route.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/api/v1/dashboard/metrics/route.js +17 -5
  47. package/.next/standalone/.next/server/app/api/v1/dashboard/pricing/route.js +7 -0
  48. package/.next/standalone/.next/server/app/api/v1/dashboard/pricing-task/route.js +1 -1
  49. package/.next/standalone/.next/server/app/api/v1/documents/search/route.js +7 -0
  50. package/.next/standalone/.next/server/app/api/v1/documents/settings/route.js +7 -0
  51. package/.next/standalone/.next/server/app/api/v1/documents/sources/[id]/route.js +1 -1
  52. package/.next/standalone/.next/server/app/api/v1/documents/sources/route.js +1 -1
  53. package/.next/standalone/.next/server/app/api/v1/env-sync/allowlist/route.js +7 -0
  54. package/.next/standalone/.next/server/app/api/v1/env-sync/route.js +7 -0
  55. package/.next/standalone/.next/server/app/api/v1/events/route.js +26 -32
  56. package/.next/standalone/.next/server/app/api/v1/events/route.js.nft.json +1 -1
  57. package/.next/standalone/.next/server/app/api/v1/extension/agents/route.js +1 -1
  58. package/.next/standalone/.next/server/app/api/v1/extension/browser/activate/route.js +7 -0
  59. package/.next/standalone/.next/server/app/api/v1/extension/browser/foreground/route.js +7 -0
  60. package/.next/standalone/.next/server/app/api/v1/extension/browser/history/route.js +7 -0
  61. package/.next/standalone/.next/server/app/api/v1/extension/browser/poll/route.js +7 -0
  62. package/.next/standalone/.next/server/app/api/v1/extension/browser/progress/route.js +7 -0
  63. package/.next/standalone/.next/server/app/api/v1/extension/browser/result/route.js +7 -0
  64. package/.next/standalone/.next/server/app/api/v1/extension/browser/retry/route.js +7 -0
  65. package/.next/standalone/.next/server/app/api/v1/extension/browser/status/route.js +7 -0
  66. package/.next/standalone/.next/server/app/api/v1/extension/browser/tabs/route.js +7 -0
  67. package/.next/standalone/.next/server/app/api/v1/extension/fill/route.js +1 -1
  68. package/.next/standalone/.next/server/app/api/v1/extension/refine/route.js +1 -1
  69. package/.next/standalone/.next/server/app/api/v1/extension/turn/route.js +1 -1
  70. package/.next/standalone/.next/server/app/api/v1/extensions/route.js +1 -1
  71. package/.next/standalone/.next/server/app/api/v1/extensions/tools/[name]/config/route.js +1 -1
  72. package/.next/standalone/.next/server/app/api/v1/extensions/tools/[name]/route.js +1 -1
  73. package/.next/standalone/.next/server/app/api/v1/extensions/tools/[name]/secrets/route.js +1 -1
  74. package/.next/standalone/.next/server/app/api/v1/harnesses/[id]/route.js +7 -0
  75. package/.next/standalone/.next/server/app/api/v1/harnesses/default/route.js +7 -0
  76. package/.next/standalone/.next/server/app/api/v1/harnesses/route.js +7 -0
  77. package/.next/standalone/.next/server/app/api/v1/health/route.js +17 -5
  78. package/.next/standalone/.next/server/app/api/v1/integrations/[name]/route.js +7 -0
  79. package/.next/standalone/.next/server/app/api/v1/integrations/gmail/oauth/callback/route.js +7 -0
  80. package/.next/standalone/.next/server/app/api/v1/integrations/gmail/oauth/start/route.js +7 -0
  81. package/.next/standalone/.next/server/app/api/v1/integrations/gmail/oauth/status/route.js +7 -0
  82. package/.next/standalone/.next/server/app/api/v1/integrations/outlook/oauth/callback/route.js +7 -0
  83. package/.next/standalone/.next/server/app/api/v1/integrations/outlook/oauth/start/route.js +7 -0
  84. package/.next/standalone/.next/server/app/api/v1/integrations/outlook/oauth/status/route.js +7 -0
  85. package/.next/standalone/.next/server/app/api/v1/integrations/route.js +7 -0
  86. package/.next/standalone/.next/server/app/api/v1/lifecycle/adoption/route.js +17 -5
  87. package/.next/standalone/.next/server/app/api/v1/maps/embed/route.js +7 -0
  88. package/.next/standalone/.next/server/app/api/v1/mcp-servers/[name]/route.js +17 -5
  89. package/.next/standalone/.next/server/app/api/v1/mcp-servers/route.js +17 -5
  90. package/.next/standalone/.next/server/app/api/v1/memory/[namespace]/[key]/route.js +7 -0
  91. package/.next/standalone/.next/server/app/api/v1/memory/policy/route.js +7 -0
  92. package/.next/standalone/.next/server/app/api/v1/memory/route.js +7 -0
  93. package/.next/standalone/.next/server/app/api/v1/memory/search/route.js +7 -0
  94. package/.next/standalone/.next/server/app/api/v1/models/[name]/compact-threads/route.js +7 -0
  95. package/.next/standalone/.next/server/app/api/v1/models/[name]/route.js +7 -0
  96. package/.next/standalone/.next/server/app/api/v1/models/route.js +7 -0
  97. package/.next/standalone/.next/server/app/api/v1/packages/defaults/[id]/route.js +28 -4
  98. package/.next/standalone/.next/server/app/api/v1/packages/reload/route.js +28 -4
  99. package/.next/standalone/.next/server/app/api/v1/packages/route.js +28 -4
  100. package/.next/standalone/.next/server/app/api/v1/page-capture/route.js +1 -1
  101. package/.next/standalone/.next/server/app/api/v1/pending-actions/[id]/approve/route.js +27 -12
  102. package/.next/standalone/.next/server/app/api/v1/profile/location/route.js +7 -0
  103. package/.next/standalone/.next/server/app/api/v1/profile/route.js +7 -0
  104. package/.next/standalone/.next/server/app/api/v1/providers/[provider]/models/route.js +585 -58
  105. package/.next/standalone/.next/server/app/api/v1/providers/[provider]/probe/route.js +7 -0
  106. package/.next/standalone/.next/server/app/api/v1/providers/github-copilot/auth/route.js +7 -0
  107. package/.next/standalone/.next/server/app/api/v1/providers/route.js +7 -0
  108. package/.next/standalone/.next/server/app/api/v1/proxy-config/route.js +7 -0
  109. package/.next/standalone/.next/server/app/api/v1/redaction/init/route.js +7 -0
  110. package/.next/standalone/.next/server/app/api/v1/redaction/route.js +7 -0
  111. package/.next/standalone/.next/server/app/api/v1/scheduled-tasks/[id]/route.js +17 -5
  112. package/.next/standalone/.next/server/app/api/v1/scheduled-tasks/[id]/run/route.js +1 -1
  113. package/.next/standalone/.next/server/app/api/v1/scheduled-tasks/route.js +7 -0
  114. package/.next/standalone/.next/server/app/api/v1/security/idle-timeout/route.js +7 -0
  115. package/.next/standalone/.next/server/app/api/v1/security/pin/route.js +7 -0
  116. package/.next/standalone/.next/server/app/api/v1/security/state/route.js +7 -0
  117. package/.next/standalone/.next/server/app/api/v1/security/unlock/route.js +7 -0
  118. package/.next/standalone/.next/server/app/api/v1/security/verify-pin/route.js +7 -0
  119. package/.next/standalone/.next/server/app/api/v1/skills/[id]/route.js +7 -0
  120. package/.next/standalone/.next/server/app/api/v1/skills/repos/[id]/route.js +7 -0
  121. package/.next/standalone/.next/server/app/api/v1/skills/repos/route.js +7 -0
  122. package/.next/standalone/.next/server/app/api/v1/skills/route.js +7 -0
  123. package/.next/standalone/.next/server/app/api/v1/tasks/[agent_id]/route.js +7 -0
  124. package/.next/standalone/.next/server/app/api/v1/tasks/route.js +7 -0
  125. package/.next/standalone/.next/server/app/api/v1/threads/[thread_id]/activity/route.js +7 -0
  126. package/.next/standalone/.next/server/app/api/v1/threads/[thread_id]/context-pin/route.js +12 -6
  127. package/.next/standalone/.next/server/app/api/v1/threads/[thread_id]/route.js +125 -26
  128. package/.next/standalone/.next/server/app/api/v1/threads/[thread_id]/run/route.js +1 -2
  129. package/.next/standalone/.next/server/app/api/v1/threads/route.js +17 -5
  130. package/.next/standalone/.next/server/app/api/v1/tools/route.js +1 -1
  131. package/.next/standalone/.next/server/app/api/v1/update/apply/route.js.nft.json +1 -1
  132. package/.next/standalone/.next/server/app/api/v1/voice/transcribe/route.js +17 -5
  133. package/.next/standalone/.next/server/app/api/v1/voice/tts/route.js +17 -5
  134. package/.next/standalone/.next/server/app/api/v1/watchers/[id]/route.js +17 -5
  135. package/.next/standalone/.next/server/app/api/v1/watchers/[id]/run/route.js +1 -1
  136. package/.next/standalone/.next/server/app/api/v1/watchers/route.js +1 -1
  137. package/.next/standalone/.next/server/app/page.js +27323 -27034
  138. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  139. package/.next/standalone/.next/server/app/setup/page.js +8 -0
  140. package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  141. package/.next/standalone/.next/server/app-paths-manifest.json +5 -5
  142. package/.next/standalone/.next/server/chunks/1388.js +106 -36
  143. package/.next/standalone/.next/server/chunks/2068.js +47 -10
  144. package/.next/standalone/.next/server/chunks/210.js +6 -6
  145. package/.next/standalone/.next/server/chunks/2447.js +10 -5
  146. package/.next/standalone/.next/server/chunks/2653.js +80 -1
  147. package/.next/standalone/.next/server/chunks/3042.js +10 -8
  148. package/.next/standalone/.next/server/chunks/{4540.js → 309.js} +104 -38
  149. package/.next/standalone/.next/server/chunks/{2350.js → 3272.js} +104 -37
  150. package/.next/standalone/.next/server/chunks/3524.js +150 -67
  151. package/.next/standalone/.next/server/chunks/3566.js +1 -1
  152. package/.next/standalone/.next/server/chunks/4189.js +1 -1
  153. package/.next/standalone/.next/server/chunks/4596.js +28 -4
  154. package/.next/standalone/.next/server/chunks/46.js +91 -31
  155. package/.next/standalone/.next/server/chunks/466.js +53 -7
  156. package/.next/standalone/.next/server/chunks/4955.js +30 -3
  157. package/.next/standalone/.next/server/chunks/5585.js +28 -4
  158. package/.next/standalone/.next/server/chunks/5908.js +63 -42
  159. package/.next/standalone/.next/server/chunks/6142.js +10 -2
  160. package/.next/standalone/.next/server/chunks/8461.js +10 -8
  161. package/.next/standalone/.next/server/chunks/9032.js +5 -5
  162. package/.next/standalone/.next/server/chunks/9164.js +487 -177
  163. package/.next/standalone/.next/server/chunks/9304.js +51 -12
  164. package/.next/standalone/.next/server/chunks/{423.js → 9590.js} +84 -24
  165. package/.next/standalone/.next/server/middleware-build-manifest.js +2 -2
  166. package/.next/standalone/.next/server/middleware.js +89 -21
  167. package/.next/standalone/.next/server/pages/404.html +14 -2
  168. package/.next/standalone/.next/server/pages/500.html +1 -1
  169. package/.next/standalone/.next/server/preview-props.json +3 -3
  170. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  171. package/.next/standalone/.next/static/chunks/{1395-e4a99912b8f83044.js → 1395-bfe1f3ef760c0f50.js} +54 -8
  172. package/.next/standalone/.next/static/chunks/app/{layout-9bc0b10127cb7d42.js → layout-7b24ad13faa3944b.js} +69 -2
  173. package/.next/standalone/.next/static/chunks/app/{page-213a623b37226452.js → page-2ef8d75d40eb4311.js} +10643 -10295
  174. package/.next/standalone/.next/static/css/c160ea862133acd2.css +5 -0
  175. package/.next/standalone/docs/adoption/changesets.json +2805 -0
  176. package/.next/standalone/package.json +1 -1
  177. package/CHANGELOG.md +52 -0
  178. package/api/client.models.test.ts +10 -0
  179. package/api/client.startup.test.ts +129 -0
  180. package/api/client.ts +51 -9
  181. package/api/thread-metadata.test.ts +72 -0
  182. package/api/types.ts +6 -3
  183. package/app/api/v1/providers/[provider]/models/route.test.ts +216 -6
  184. package/app/api/v1/providers/[provider]/models/route.ts +259 -40
  185. package/app/api/v1/threads/[thread_id]/route.ts +26 -23
  186. package/app/api/v1/threads/[thread_id]/run/route.ts +0 -1
  187. package/app/globals.css +119 -29
  188. package/app/layout.tsx +12 -0
  189. package/components/agents/AgentsPanel.tsx +5 -13
  190. package/components/chat/ChatView.tsx +9 -18
  191. package/components/chat/InputBar.tsx +1 -1
  192. package/components/chat/MessageBubble.tsx +13 -1
  193. package/components/chat/MessageList.test.tsx +45 -4
  194. package/components/chat/MessageList.tsx +123 -89
  195. package/components/chat/ToolList.tsx +88 -84
  196. package/components/chat/useThreadActivity.test.tsx +2 -2
  197. package/components/chat/useThreadActivity.ts +2 -2
  198. package/components/chat/useThreadData.ts +1 -30
  199. package/components/chat/useThreadMetadataPolling.test.tsx +128 -0
  200. package/components/chat/useThreadMetadataPolling.ts +50 -0
  201. package/components/chat/useUserProfileAndAgent.test.tsx +31 -0
  202. package/components/chat/useUserProfileAndAgent.ts +13 -2
  203. package/components/dashboard/DashboardPanel.tsx +1 -1
  204. package/components/dashboard/Series.tsx +34 -21
  205. package/components/dashboard/SummaryCards.tsx +1 -1
  206. package/components/integrations/NetworkSection.tsx +2 -2
  207. package/components/layout/AppShell.tsx +22 -7
  208. package/components/layout/MenuPanel.tsx +14 -24
  209. package/components/profile/ProfilePanel.tsx +2 -2
  210. package/components/settings/AppearancePanel.tsx +70 -3
  211. package/components/settings/SettingsPanel.tsx +2 -2
  212. package/components/tools/ToolsPanel.tsx +2 -7
  213. package/components/ui/AppearancePreferences.test.tsx +68 -0
  214. package/components/ui/BootScreen.test.tsx +44 -0
  215. package/components/ui/BootScreen.tsx +24 -11
  216. package/components/ui/NotificationStatus.test.tsx +2 -2
  217. package/components/ui/NotificationStatus.tsx +5 -5
  218. package/components/ui/SubTabBar.tsx +2 -3
  219. package/contexts/ThemeContext.tsx +84 -1
  220. package/hooks/useAgentSession.test.ts +12 -1
  221. package/hooks/useAgentSession.ts +6 -4
  222. package/hooks/useUrlSync.test.ts +13 -0
  223. package/hooks/useUrlSync.ts +0 -4
  224. package/lib/agents/citation-checker.test.ts +40 -2
  225. package/lib/agents/citation-checker.ts +56 -11
  226. package/lib/agents/context-boundary.ts +5 -6
  227. package/lib/agents/instruction-edits.test.ts +120 -0
  228. package/lib/agents/instruction-edits.ts +65 -42
  229. package/lib/agents/prepare/request.ts +0 -2
  230. package/lib/agents/prepare/system-prompt.test.ts +10 -0
  231. package/lib/agents/prepare/system-prompt.ts +8 -2
  232. package/lib/agents/proposals.test.ts +76 -0
  233. package/lib/agents/proposals.ts +13 -5
  234. package/lib/agents/run-thread.auto-boundary.test.ts +125 -5
  235. package/lib/agents/run-thread.ts +24 -9
  236. package/lib/agents/sqlite-checkpoint-saver.test.ts +91 -0
  237. package/lib/agents/warm-summary-background.ts +22 -2
  238. package/lib/agents/warm-topics.test.ts +155 -1
  239. package/lib/agents/warm-topics.ts +27 -7
  240. package/lib/bridges/dispatcher.ts +1 -0
  241. package/lib/bridges/message-role.test.ts +81 -0
  242. package/lib/bridges/message-role.ts +23 -10
  243. package/lib/bridges/types.ts +6 -0
  244. package/lib/bridges/whatsapp-link.test.ts +32 -0
  245. package/lib/bridges/whatsapp-link.ts +21 -0
  246. package/lib/bridges/whatsapp.inbound.test.ts +239 -0
  247. package/lib/bridges/whatsapp.sender.test.ts +80 -1
  248. package/lib/bridges/whatsapp.ts +76 -38
  249. package/lib/db/migrations.test.ts +61 -0
  250. package/lib/db/migrations.ts +13 -0
  251. package/lib/embeddings/performance.test.ts +114 -0
  252. package/lib/pricing/snapshot.test.ts +70 -0
  253. package/lib/pricing/snapshot.ts +10 -2
  254. package/lib/proxy/dispatcher.test.ts +119 -1
  255. package/lib/proxy/dispatcher.ts +22 -9
  256. package/lib/stores/agent-configs.ts +10 -5
  257. package/lib/stores/bridges.test.ts +81 -0
  258. package/lib/stores/bridges.ts +9 -8
  259. package/lib/stores/dashboard-metrics-cache.test.ts +99 -0
  260. package/lib/stores/dashboard-metrics.ts +30 -4
  261. package/lib/stores/message-embedding-jobs.test.ts +27 -0
  262. package/lib/stores/message-embedding-jobs.ts +5 -3
  263. package/lib/stores/message-usage.test.ts +21 -1
  264. package/lib/stores/pricing-cache.test.ts +31 -0
  265. package/lib/stores/pricing.ts +3 -0
  266. package/lib/stores/proxy-config.test.ts +120 -0
  267. package/lib/stores/proxy-config.ts +22 -4
  268. package/lib/stores/threads.ts +12 -8
  269. package/lib/tools/communications/calendar.test.ts +43 -2
  270. package/lib/tools/communications/calendar.ts +2 -0
  271. package/lib/tools/communications/gmail.test.ts +51 -1
  272. package/lib/tools/communications/gmail.ts +44 -2
  273. package/lib/tools/communications/outlook-calendar.ts +2 -0
  274. package/lib/tools/communications/outlook.test.ts +33 -2
  275. package/lib/tools/communications/outlook.ts +2 -0
  276. package/lib/tools/filesystem/edit-match.test.ts +133 -0
  277. package/lib/tools/filesystem/edit-match.ts +7 -2
  278. package/lib/tools/packages/default-packages.test.ts +21 -0
  279. package/lib/tools/packages/optional-package.test.ts +72 -0
  280. package/lib/tools/packages/optional-package.ts +33 -4
  281. package/lib/tools/support/result-refs.test.ts +131 -0
  282. package/lib/tools/support/result-refs.ts +41 -9
  283. package/lib/ui/navigate.test.ts +13 -0
  284. package/lib/ui/navigate.ts +10 -2
  285. package/lib/utils/source-link.test.ts +51 -0
  286. package/lib/utils/source-link.ts +45 -0
  287. package/package.json +1 -1
  288. package/.next/standalone/.next/static/css/312542a5725ba0d5.css +0 -5
  289. /package/.next/standalone/.next/static/{bMRK5y3ruT7c3WtBfCQOG → 544OFJV-q0RC6Gjgje4LT}/_buildManifest.js +0 -0
  290. /package/.next/standalone/.next/static/{bMRK5y3ruT7c3WtBfCQOG → 544OFJV-q0RC6Gjgje4LT}/_ssgManifest.js +0 -0
@@ -64,6 +64,9 @@ function runMigrations(db) {
64
64
  -- (getMessages, getRecentMessagesWindow, getMessagesPage, clear).
65
65
  -- Without this every read full-scans the messages table.
66
66
  CREATE INDEX IF NOT EXISTS idx_messages_thread_id ON messages(thread_id, created_at);
67
+ CREATE INDEX IF NOT EXISTS idx_messages_thread_seq ON messages(thread_id);
68
+ CREATE INDEX IF NOT EXISTS idx_messages_created_at ON messages(created_at);
69
+ CREATE INDEX IF NOT EXISTS idx_threads_updated ON threads(updated_at DESC);
67
70
  CREATE TABLE IF NOT EXISTS runtime_operations (
68
71
  id TEXT PRIMARY KEY,
69
72
  kind TEXT NOT NULL,
@@ -87,6 +90,9 @@ function runMigrations(db) {
87
90
  created_at INTEGER NOT NULL,
88
91
  UNIQUE(kind,operation_id,agent_id,thread_id)
89
92
  );
93
+ CREATE INDEX IF NOT EXISTS idx_runtime_operations_thread_state ON runtime_operations(thread_id,state);
94
+ CREATE INDEX IF NOT EXISTS idx_signal_events_thread ON signal_events(thread_id);
95
+ CREATE INDEX IF NOT EXISTS idx_signal_events_operation ON signal_events(operation_id);
90
96
  CREATE TABLE IF NOT EXISTS signal_deliveries (
91
97
  event_id TEXT PRIMARY KEY REFERENCES signal_events(id) ON DELETE CASCADE,
92
98
  wake_eligible INTEGER NOT NULL DEFAULT 0,
@@ -101,6 +107,7 @@ function runMigrations(db) {
101
107
  );
102
108
  CREATE INDEX IF NOT EXISTS idx_signal_events_target ON signal_events(agent_id,thread_id,seq);
103
109
  CREATE INDEX IF NOT EXISTS idx_signal_deliveries_due ON signal_deliveries(state,available_at);
110
+ CREATE INDEX IF NOT EXISTS idx_signal_deliveries_lease ON signal_deliveries(state,lease_until);
104
111
  CREATE TABLE IF NOT EXISTS background_tool_results (
105
112
  key TEXT PRIMARY KEY REFERENCES runtime_operations(id) ON DELETE CASCADE,
106
113
  thread_id TEXT NOT NULL REFERENCES threads(thread_id) ON DELETE CASCADE,
@@ -112,6 +119,7 @@ function runMigrations(db) {
112
119
  expires_at INTEGER NOT NULL
113
120
  );
114
121
  CREATE INDEX IF NOT EXISTS idx_background_results_owner ON background_tool_results(thread_id,expires_at);
122
+ CREATE INDEX IF NOT EXISTS idx_background_results_expiry ON background_tool_results(expires_at);
115
123
  CREATE TABLE IF NOT EXISTS memory_store (
116
124
  namespace TEXT NOT NULL,
117
125
  key TEXT NOT NULL,
@@ -120,6 +128,8 @@ function runMigrations(db) {
120
128
  updated_at TEXT NOT NULL,
121
129
  PRIMARY KEY (namespace, key)
122
130
  );
131
+ CREATE INDEX IF NOT EXISTS idx_memory_namespace_updated ON memory_store(namespace,updated_at DESC);
132
+ CREATE INDEX IF NOT EXISTS idx_memory_updated ON memory_store(updated_at DESC);
123
133
  CREATE TABLE IF NOT EXISTS model_configs (
124
134
  name TEXT PRIMARY KEY,
125
135
  provider TEXT NOT NULL,
@@ -693,6 +703,7 @@ function ensureMessageUsageTable(db) {
693
703
  );
694
704
  CREATE INDEX IF NOT EXISTS idx_message_usage_created_at ON message_usage(created_at);
695
705
  CREATE INDEX IF NOT EXISTS idx_message_usage_agent_id ON message_usage(agent_id);
706
+ CREATE INDEX IF NOT EXISTS idx_message_usage_thread_created ON message_usage(thread_id,created_at DESC);
696
707
  `);
697
708
  }
698
709
  function ensureEmbeddingColumns(db) {
@@ -732,6 +743,8 @@ function ensureMessageEmbeddingJobsTable(db) {
732
743
  );
733
744
  CREATE INDEX IF NOT EXISTS idx_message_embedding_jobs_due
734
745
  ON message_embedding_jobs(state, available_at_ms, lease_until_ms);
746
+ CREATE INDEX IF NOT EXISTS idx_message_embedding_jobs_lease
747
+ ON message_embedding_jobs(state, lease_until_ms);
735
748
  INSERT OR IGNORE INTO message_embedding_jobs (message_id, created_at, updated_at)
736
749
  SELECT msg_id, created_at, created_at FROM messages WHERE embedding IS NULL;
737
750
  `);
@@ -6155,9 +6168,11 @@ function claimMessageEmbeddingJobs(limit = 16, leaseMs = 5 * 60000, nowMs = Date
6155
6168
  });
6156
6169
  }
6157
6170
  function nextMessageEmbeddingDelayMs(nowMs = Date.now()) {
6158
- const row = (0,_lib_db__WEBPACK_IMPORTED_MODULE_2__/* .getDb */ .Lf)().prepare(`SELECT MIN(CASE WHEN state='pending' THEN available_at_ms ELSE lease_until_ms END) AS next_at_ms
6159
- FROM message_embedding_jobs
6160
- WHERE state IN ('pending','processing')`).get();
6171
+ const row = (0,_lib_db__WEBPACK_IMPORTED_MODULE_2__/* .getDb */ .Lf)().prepare(`SELECT MIN(next_at_ms) AS next_at_ms FROM (
6172
+ SELECT MIN(available_at_ms) AS next_at_ms FROM message_embedding_jobs WHERE state='pending'
6173
+ UNION ALL
6174
+ SELECT MIN(lease_until_ms) AS next_at_ms FROM message_embedding_jobs WHERE state='processing'
6175
+ )`).get();
6161
6176
  return typeof row?.next_at_ms === "number" ? Math.max(0, row.next_at_ms - nowMs) : null;
6162
6177
  }
6163
6178
  function completeMessageEmbeddingJob(job, embedding, modelSignature) {
@@ -8680,7 +8695,25 @@ async function applyProxyConfigFromDb() {
8680
8695
  // ---------------------------------------------------------------------
8681
8696
  function buildProxyUrl(scheme, host, port, username, password) {
8682
8697
  const auth = username ? `${encodeURIComponent(username)}${password ? `:${encodeURIComponent(password)}` : ""}@` : "";
8683
- return `${scheme}://${auth}${host}:${port}`;
8698
+ const urlHost = host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
8699
+ return `${scheme}://${auth}${urlHost}:${port}`;
8700
+ }
8701
+ // undici's EnvHttpProxyAgent has no implicit loopback exemption, so without
8702
+ // these a configured proxy would also receive local traffic (Ollama, the
8703
+ // WhatsApp bridge sidecar, the app's own loopback API).
8704
+ const LOOPBACK_NO_PROXY = [
8705
+ "localhost",
8706
+ "127.0.0.1",
8707
+ "::1"
8708
+ ];
8709
+ function mergeNoProxy(noProxy) {
8710
+ const entries = (noProxy ?? "").split(/[,\s]+/).map((entry)=>entry.trim()).filter(Boolean);
8711
+ return [
8712
+ ...new Set([
8713
+ ...entries,
8714
+ ...LOOPBACK_NO_PROXY
8715
+ ])
8716
+ ].join(",");
8684
8717
  }
8685
8718
  function installProxy(url, noProxy, caBundle) {
8686
8719
  // Set process.env so EnvHttpProxyAgent honors NO_PROXY semantics
@@ -8688,27 +8721,31 @@ function installProxy(url, noProxy, caBundle) {
8688
8721
  // We still treat the boot-time env snapshot as authoritative for the
8689
8722
  // "env beats DB" rule — this mutation only happens after we've
8690
8723
  // confirmed the user did NOT set HTTP_PROXY at launch.
8724
+ const merged = mergeNoProxy(noProxy);
8691
8725
  process.env.HTTP_PROXY = url;
8692
8726
  process.env.HTTPS_PROXY = url;
8693
- if (noProxy && noProxy.trim().length > 0) {
8694
- process.env.NO_PROXY = noProxy.trim();
8695
- } else {
8696
- delete process.env.NO_PROXY;
8697
- }
8727
+ process.env.NO_PROXY = merged;
8698
8728
  // ADR-0012: when the user pasted a corporate root CA, inject it on
8699
8729
  // both TLS legs — the proxy hop (proxyTls) for https-scheme proxies
8700
8730
  // signed by the corporate root, and the upstream tunnel (requestTls)
8701
8731
  // for proxies that MITM outbound HTTPS with the same internal CA.
8702
8732
  // Same blob is correct: the corporate trust chain typically signs
8703
8733
  // both ends.
8704
- const opts = caBundle ? {
8705
- requestTls: {
8706
- ca: caBundle
8707
- },
8708
- proxyTls: {
8709
- ca: caBundle
8710
- }
8711
- } : {};
8734
+ // Pass the values explicitly too: undici prefers lowercase http_proxy /
8735
+ // no_proxy from the environment over the uppercase ones set above.
8736
+ const opts = {
8737
+ httpProxy: url,
8738
+ httpsProxy: url,
8739
+ noProxy: merged,
8740
+ ...caBundle ? {
8741
+ requestTls: {
8742
+ ca: caBundle
8743
+ },
8744
+ proxyTls: {
8745
+ ca: caBundle
8746
+ }
8747
+ } : {}
8748
+ };
8712
8749
  (0,external_undici_.setGlobalDispatcher)(new external_undici_.EnvHttpProxyAgent(opts));
8713
8750
  }
8714
8751
  function resetToBaseline() {
@@ -10776,9 +10813,11 @@ function getIntegrationMeta(name) {
10776
10813
  /* harmony export */ qt: () => (/* binding */ saveProxyConfig),
10777
10814
  /* harmony export */ xl: () => (/* binding */ getProxyConfigRaw)
10778
10815
  /* harmony export */ });
10779
- /* harmony import */ var _lib_db__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(46);
10780
- /* harmony import */ var _lib_crypto_envelope__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(10938);
10781
- /* harmony import */ var _lib_stores_integrations__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(78228);
10816
+ /* harmony import */ var node_net__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(77030);
10817
+ /* harmony import */ var node_net__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(node_net__WEBPACK_IMPORTED_MODULE_0__);
10818
+ /* harmony import */ var _lib_db__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(46);
10819
+ /* harmony import */ var _lib_crypto_envelope__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(10938);
10820
+ /* harmony import */ var _lib_stores_integrations__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(78228);
10782
10821
  // Proxy configuration store (ADR-0009, extended ADR-0012).
10783
10822
  //
10784
10823
  // Single-row table proxy_config. Non-secret columns are plaintext for
@@ -10798,6 +10837,7 @@ function getIntegrationMeta(name) {
10798
10837
 
10799
10838
 
10800
10839
 
10840
+
10801
10841
  const DEFAULT_STATUS = {
10802
10842
  mode: "off",
10803
10843
  scheme: "http",
@@ -10818,7 +10858,7 @@ function getProxyConfigStatus() {
10818
10858
  host: row.host,
10819
10859
  port: row.port,
10820
10860
  username: row.username,
10821
- password: row.password ? _lib_stores_integrations__WEBPACK_IMPORTED_MODULE_2__/* .SECRET_MASK */ .Q6 : null,
10861
+ password: row.password ? _lib_stores_integrations__WEBPACK_IMPORTED_MODULE_3__/* .SECRET_MASK */ .Q6 : null,
10822
10862
  no_proxy: row.no_proxy,
10823
10863
  ca_bundle: row.ca_bundle,
10824
10864
  updated_at: row.updated_at
@@ -10836,7 +10876,7 @@ function getProxyConfigRaw() {
10836
10876
  host: row.host,
10837
10877
  port: row.port,
10838
10878
  username: row.username,
10839
- password: row.password ? (0,_lib_crypto_envelope__WEBPACK_IMPORTED_MODULE_1__/* .decryptIfNeeded */ .qU)(row.password) : null,
10879
+ password: row.password ? (0,_lib_crypto_envelope__WEBPACK_IMPORTED_MODULE_2__/* .decryptIfNeeded */ .qU)(row.password) : null,
10840
10880
  no_proxy: row.no_proxy,
10841
10881
  ca_bundle: row.ca_bundle,
10842
10882
  updated_at: row.updated_at
@@ -10848,15 +10888,26 @@ function saveProxyConfig(input) {
10848
10888
  };
10849
10889
  const scheme = input.scheme === "https" ? "https" : "http";
10850
10890
  // Manual mode requires host + port. system / off don't.
10891
+ let host = nullable(input.host);
10851
10892
  if (input.mode === "manual") {
10852
- if (!input.host || !input.host.trim()) return {
10893
+ if (!host) return {
10853
10894
  error: "host is required in manual mode"
10854
10895
  };
10855
- if (input.port == null || !Number.isFinite(input.port) || input.port <= 0 || input.port > 65535) {
10896
+ const normalisedHost = normaliseProxyHost(host);
10897
+ if (!normalisedHost) {
10856
10898
  return {
10857
- error: "port must be between 1 and 65535"
10899
+ error: "host must be a hostname or IP address, without scheme, port, path or credentials"
10858
10900
  };
10859
10901
  }
10902
+ host = normalisedHost;
10903
+ if (input.port == null) return {
10904
+ error: "port must be between 1 and 65535"
10905
+ };
10906
+ }
10907
+ if (input.port != null && (!Number.isInteger(input.port) || input.port <= 0 || input.port > 65535)) {
10908
+ return {
10909
+ error: "port must be between 1 and 65535"
10910
+ };
10860
10911
  }
10861
10912
  // CA bundle: empty/null clears; non-empty must look like PEM.
10862
10913
  let storedCaBundle = null;
@@ -10871,15 +10922,15 @@ function saveProxyConfig(input) {
10871
10922
  // Preserve existing password when the UI echoes back the masked sentinel.
10872
10923
  const existing = readRow();
10873
10924
  let storedPassword = null;
10874
- if (input.password === _lib_stores_integrations__WEBPACK_IMPORTED_MODULE_2__/* .SECRET_MASK */ .Q6) {
10925
+ if (input.password === _lib_stores_integrations__WEBPACK_IMPORTED_MODULE_3__/* .SECRET_MASK */ .Q6) {
10875
10926
  storedPassword = existing?.password ?? null;
10876
10927
  } else if (input.password && input.password.length > 0) {
10877
- storedPassword = (0,_lib_crypto_envelope__WEBPACK_IMPORTED_MODULE_1__/* .encrypt */ .w)(input.password);
10928
+ storedPassword = (0,_lib_crypto_envelope__WEBPACK_IMPORTED_MODULE_2__/* .encrypt */ .w)(input.password);
10878
10929
  } else {
10879
10930
  storedPassword = null;
10880
10931
  }
10881
10932
  const now = new Date().toISOString();
10882
- const db = (0,_lib_db__WEBPACK_IMPORTED_MODULE_0__/* .getDb */ .Lf)();
10933
+ const db = (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)();
10883
10934
  db.prepare(`INSERT INTO proxy_config (id, mode, scheme, host, port, username, password, no_proxy, ca_bundle, updated_at)
10884
10935
  VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?, ?)
10885
10936
  ON CONFLICT(id) DO UPDATE SET
@@ -10891,16 +10942,16 @@ function saveProxyConfig(input) {
10891
10942
  password = excluded.password,
10892
10943
  no_proxy = excluded.no_proxy,
10893
10944
  ca_bundle = excluded.ca_bundle,
10894
- updated_at = excluded.updated_at`).run(input.mode, scheme, nullable(input.host), input.port ?? null, nullable(input.username), storedPassword, nullable(input.no_proxy), storedCaBundle, now);
10945
+ updated_at = excluded.updated_at`).run(input.mode, scheme, host, input.port ?? null, nullable(input.username), storedPassword, nullable(input.no_proxy), storedCaBundle, now);
10895
10946
  return getProxyConfigStatus();
10896
10947
  }
10897
10948
  function deleteProxyConfig() {
10898
- const db = (0,_lib_db__WEBPACK_IMPORTED_MODULE_0__/* .getDb */ .Lf)();
10949
+ const db = (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)();
10899
10950
  const r = db.prepare("DELETE FROM proxy_config WHERE id = 1").run();
10900
10951
  return r.changes > 0;
10901
10952
  }
10902
10953
  function readRow() {
10903
- const db = (0,_lib_db__WEBPACK_IMPORTED_MODULE_0__/* .getDb */ .Lf)();
10954
+ const db = (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)();
10904
10955
  const r = db.prepare("SELECT mode, scheme, host, port, username, password, no_proxy, ca_bundle, updated_at FROM proxy_config WHERE id = 1").get();
10905
10956
  return r ?? null;
10906
10957
  }
@@ -10913,6 +10964,15 @@ function normaliseMode(m) {
10913
10964
  function normaliseScheme(s) {
10914
10965
  return s === "https" ? "https" : "http";
10915
10966
  }
10967
+ // The dispatcher builds `${scheme}://${auth}${host}:${port}`, so a pasted URL
10968
+ // or host:port would produce an unusable proxy URL. IPv6 is stored unbracketed;
10969
+ // the dispatcher adds the brackets.
10970
+ function normaliseProxyHost(host) {
10971
+ const unbracketed = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
10972
+ if (/^[A-Za-z0-9](?:[A-Za-z0-9.-]*[A-Za-z0-9])?$/.test(unbracketed)) return unbracketed;
10973
+ if ((0,node_net__WEBPACK_IMPORTED_MODULE_0__.isIPv6)(unbracketed)) return unbracketed;
10974
+ return null;
10975
+ }
10916
10976
  function nullable(v) {
10917
10977
  if (v == null) return null;
10918
10978
  const trimmed = v.trim();
@@ -734,11 +734,44 @@ async function parseJsonResponse(res) {
734
734
  const stream = source.pipeThrough(new DecompressionStream("gzip"));
735
735
  return JSON.parse(await new Response(stream).text());
736
736
  }
737
+ const STARTUP_TTL_MS = 5000;
738
+ const startupReads = new Map();
739
+ function startupRead(path) {
740
+ const now = Date.now();
741
+ for (const [key, entry] of startupReads){
742
+ if (entry.settledAt !== null && (now < entry.settledAt || now - entry.settledAt >= STARTUP_TTL_MS)) {
743
+ startupReads.delete(key);
744
+ }
745
+ }
746
+ const cached = startupReads.get(path);
747
+ if (cached) return cached.promise.then((value)=>structuredClone(value));
748
+ // Bound retained startup data when agents are switched rapidly.
749
+ const oldestKey = startupReads.keys().next().value;
750
+ if (startupReads.size >= 16 && oldestKey !== undefined) startupReads.delete(oldestKey);
751
+ const entry = {
752
+ promise: Promise.resolve(undefined),
753
+ settledAt: null
754
+ };
755
+ entry.promise = request(path, {
756
+ startup: true
757
+ }).then((value)=>{
758
+ entry.settledAt = Date.now();
759
+ return value;
760
+ }).catch((error)=>{
761
+ if (startupReads.get(path) === entry) startupReads.delete(path);
762
+ throw error;
763
+ });
764
+ startupReads.set(path, entry);
765
+ return entry.promise.then((value)=>structuredClone(value));
766
+ }
737
767
  async function request(path, init) {
768
+ const mutation = !!init?.method && init.method !== "GET";
769
+ if (mutation) startupReads.clear();
770
+ else if (!init?.startup) startupReads.delete(path);
738
771
  const cfg = (0,_runtime_config__WEBPACK_IMPORTED_MODULE_0__/* .runtimeConfig */ .Z)();
739
772
  const maxAttempts = Math.max(1, cfg.httpMaxAttempts);
740
773
  const timeoutMs = init?.timeoutMs ?? cfg.httpRequestTimeoutMs;
741
- const { timeoutMs: _ignoreTimeoutMs, ...fetchInit } = init ?? {};
774
+ const { timeoutMs: _ignoreTimeoutMs, startup: _ignoreStartup, ...fetchInit } = init ?? {};
742
775
  const callerSignal = fetchInit.signal ?? null;
743
776
  let lastErr = null;
744
777
  for(let attempt = 1; attempt <= maxAttempts; attempt++){
@@ -792,6 +825,7 @@ async function request(path, init) {
792
825
  if (attempt < maxAttempts && isRetryable(err)) continue;
793
826
  throw err;
794
827
  } finally{
828
+ if (mutation) startupReads.clear();
795
829
  clearTimeout(timer);
796
830
  callerSignal?.removeEventListener("abort", onCallerAbort);
797
831
  }
@@ -802,7 +836,10 @@ async function request(path, init) {
802
836
  const api = {
803
837
  agents: {
804
838
  list: (opts)=>cachedList(agentListCache, ()=>request("/agents"), setAgentListCache, opts?.force === true),
805
- get: (id)=>request(`/agents/${encodeURIComponent(id)}`),
839
+ get: (id, opts)=>{
840
+ const path = `/agents/${encodeURIComponent(id)}`;
841
+ return opts?.startup ? startupRead(path) : request(path);
842
+ },
806
843
  create: async (data)=>{
807
844
  const created = await request("/agents", {
808
845
  method: "POST",
@@ -834,7 +871,10 @@ const api = {
834
871
  }
835
872
  return res;
836
873
  },
837
- getThread: (id)=>request(`/agents/${encodeURIComponent(id)}/thread`),
874
+ getThread: (id, opts)=>{
875
+ const path = `/agents/${encodeURIComponent(id)}/thread`;
876
+ return opts?.startup ? startupRead(path) : request(path);
877
+ },
838
878
  compact: (id, options)=>request(`/agents/${encodeURIComponent(id)}/compact`, // Conversation summarization can take longer than normal API calls,
839
879
  // so avoid tripping the default HTTP timeout for large threads.
840
880
  {
@@ -1053,8 +1093,12 @@ const api = {
1053
1093
  // user+assistant pair instead of re-pulling the whole most-recent page.
1054
1094
  if (opts?.after !== undefined) p.set("after", String(opts.after));
1055
1095
  const qs = p.toString() ? `?${p}` : "";
1056
- return request(`/threads/${thread_id}${qs}`);
1096
+ const path = `/threads/${thread_id}${qs}`;
1097
+ return opts?.startup ? startupRead(path) : request(path);
1057
1098
  },
1099
+ metadata: (thread_id, signal)=>request(`/threads/${thread_id}?view=metadata`, {
1100
+ signal
1101
+ }),
1058
1102
  delete: (thread_id)=>request(`/threads/${thread_id}`, {
1059
1103
  method: "DELETE"
1060
1104
  }),
@@ -1253,7 +1297,7 @@ const api = {
1253
1297
  }
1254
1298
  },
1255
1299
  profile: {
1256
- get: ()=>request("/profile"),
1300
+ get: (opts)=>opts?.startup ? startupRead("/profile") : request("/profile"),
1257
1301
  update: (data)=>request("/profile", {
1258
1302
  method: "PUT",
1259
1303
  body: JSON.stringify(data)
@@ -1737,6 +1781,7 @@ const api = {
1737
1781
  * caller is expected to follow up with `subscribeRun()` to receive the
1738
1782
  * chunk stream. Idempotent in the sense that two simultaneous submissions
1739
1783
  * for the same thread will see one 202 and one 409 (`run_in_flight`). */ async function submitRun(thread_id, message, signal, stream_options, attachments, channels, hot_since_seq) {
1784
+ startupReads.clear();
1740
1785
  const payload = {
1741
1786
  message,
1742
1787
  stream_options,
@@ -1752,7 +1797,7 @@ const api = {
1752
1797
  },
1753
1798
  body: JSON.stringify(runPayload),
1754
1799
  signal
1755
- });
1800
+ }).finally(()=>startupReads.clear());
1756
1801
  // 2xx = accepted (currently always 202); 409 = already running. We treat
1757
1802
  // every other status as a hard error so the consumer's catch fires.
1758
1803
  if (res.status === 202) {
@@ -9841,6 +9886,7 @@ const Select = /*#__PURE__*/ react__WEBPACK_IMPORTED_MODULE_1___default().forwar
9841
9886
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
9842
9887
 
9843
9888
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
9889
+ /* harmony export */ Dg: () => (/* binding */ getAppAccentColor),
9844
9890
  /* harmony export */ E2: () => (/* binding */ UPSTREAM_URL),
9845
9891
  /* harmony export */ SN: () => (/* binding */ getAppIssueUrl),
9846
9892
  /* harmony export */ YC: () => (/* binding */ UPSTREAM_NAME),
@@ -9849,7 +9895,7 @@ const Select = /*#__PURE__*/ react__WEBPACK_IMPORTED_MODULE_1___default().forwar
9849
9895
  /* harmony export */ lE: () => (/* binding */ getAppLogoDark),
9850
9896
  /* harmony export */ qc: () => (/* binding */ getAppIcons)
9851
9897
  /* harmony export */ });
9852
- /* unused harmony exports getAppShortName, getAppDescription, getAppAccentColor, getAppAccentHoverColor */
9898
+ /* unused harmony exports getAppShortName, getAppDescription, getAppAccentHoverColor */
9853
9899
  // Branding knobs sourced from NEXT_PUBLIC_* env vars so forks can rebrand
9854
9900
  // the app without patching source. NEXT_PUBLIC_* is the right
9855
9901
  // channel here because Next.js inlines these at build time, which lets
@@ -18,6 +18,8 @@ exports.modules = {
18
18
 
19
19
  const CHARS_PER_TOKEN = 4;
20
20
  const DEFAULT_WINDOW_DAYS = 30;
21
+ const METRICS_CACHE_MS = 5000;
22
+ let metricsCache = null;
21
23
  function recalculateSnapshotCost(input, tables) {
22
24
  const rates = (0,_pricing__WEBPACK_IMPORTED_MODULE_2__/* .modelRatesFor */ .En)(tables, input.provider, input.modelId);
23
25
  if (rates.inputPer1M == null && rates.outputPer1M == null) return input.recordedCostUsd;
@@ -30,7 +32,17 @@ async function getDashboardMetrics(days = DEFAULT_WINDOW_DAYS) {
30
32
  const now = new Date();
31
33
  const boundedDays = Number.isFinite(days) ? Math.min(120, Math.max(7, Math.floor(days))) : DEFAULT_WINDOW_DAYS;
32
34
  const since = new Date(now.getTime() - boundedDays * 24 * 60 * 60 * 1000).toISOString();
33
- const usageRows = (0,_lib_db__WEBPACK_IMPORTED_MODULE_0__/* .getDb */ .Lf)().prepare(`SELECT m.created_at, m.role, m.content, m.tool_events, m.thread_id, t.agent_id,
35
+ const db = (0,_lib_db__WEBPACK_IMPORTED_MODULE_0__/* .getDb */ .Lf)();
36
+ const tables = (0,_pricing__WEBPACK_IMPORTED_MODULE_2__/* .getPricingTables */ .iH)();
37
+ // total_changes covers this connection; data_version covers other writers.
38
+ const { changes } = db.prepare("SELECT total_changes() AS changes").get();
39
+ const { data_version: dataVersion } = db.prepare("PRAGMA data_version").get();
40
+ if (metricsCache?.db === db && metricsCache.changes === changes && metricsCache.dataVersion === dataVersion && metricsCache.tables === tables && metricsCache.metrics.days === boundedDays && metricsCache.metrics.generated_at.slice(0, 10) === now.toISOString().slice(0, 10) && now.getTime() - metricsCache.createdAt >= 0 && now.getTime() - metricsCache.createdAt < METRICS_CACHE_MS) {
41
+ return metricsCache.metrics;
42
+ }
43
+ const usageRows = db.prepare(`SELECT m.created_at, m.role,
44
+ CASE WHEN mu.input_tokens IS NULL THEN m.content ELSE '' END AS content,
45
+ m.tool_events, m.thread_id, t.agent_id,
34
46
  a.name AS agent_name, mc.provider AS provider,
35
47
  mc.model_id AS model_id,
36
48
  COALESCE(ta.model_config_name, a.model_config_name, dmc.name) AS model_config_name,
@@ -80,7 +92,6 @@ async function getDashboardMetrics(days = DEFAULT_WINDOW_DAYS) {
80
92
  snapshotThreadIds.add(r.thread_id);
81
93
  }
82
94
  }
83
- const tables = (0,_pricing__WEBPACK_IMPORTED_MODULE_2__/* .getPricingTables */ .iH)();
84
95
  const { byProvider, generatedAt } = tables;
85
96
  const dayMap = seedDayBuckets(now, boundedDays);
86
97
  const agentMap = new Map();
@@ -396,7 +407,7 @@ async function getDashboardMetrics(days = DEFAULT_WINDOW_DAYS) {
396
407
  by_model: dayModels
397
408
  };
398
409
  }
399
- return {
410
+ const metrics = {
400
411
  generated_at: now.toISOString(),
401
412
  days: boundedDays,
402
413
  summary: {
@@ -452,6 +463,15 @@ async function getDashboardMetrics(days = DEFAULT_WINDOW_DAYS) {
452
463
  notes: "Dashboard cost is recalculated from captured provider token usage and the latest matching rates. The original per-turn cost snapshots remain unchanged; legacy rows use content-length token estimates."
453
464
  }
454
465
  };
466
+ metricsCache = {
467
+ db,
468
+ changes,
469
+ dataVersion,
470
+ tables,
471
+ createdAt: now.getTime(),
472
+ metrics
473
+ };
474
+ return metrics;
455
475
  }
456
476
  function seedDayBuckets(now, days) {
457
477
  const out = new Map();
@@ -882,6 +902,13 @@ function readSnapshotSync() {
882
902
  try {
883
903
  const p = snapshotPath();
884
904
  const stat = (0,external_node_fs_.statSync)(p);
905
+ if (cached?.path === p && cached.mtimeMs === stat.mtimeMs) {
906
+ return {
907
+ path: p,
908
+ snapshot: null,
909
+ mtimeMs: stat.mtimeMs
910
+ };
911
+ }
885
912
  const raw = (0,external_node_fs_.readFileSync)(p, "utf8");
886
913
  return {
887
914
  path: p,
@@ -335,12 +335,35 @@ function createRuntimeRequire(anchor) {
335
335
  function managedPackagePath(packageName) {
336
336
  return (0,node_path__WEBPACK_IMPORTED_MODULE_0__.join)((0,_langchain_packages__WEBPACK_IMPORTED_MODULE_1__/* .getPackagesDir */ .V_)(), "node_modules", packageName);
337
337
  }
338
+ function resolveOptionalPackage(packageName) {
339
+ const managedReq = createRuntimeRequire((0,node_path__WEBPACK_IMPORTED_MODULE_0__.join)((0,_langchain_packages__WEBPACK_IMPORTED_MODULE_1__/* .getPackagesDir */ .V_)(), "_anchor"));
340
+ try {
341
+ return {
342
+ req: managedReq,
343
+ path: managedReq.resolve(managedPackagePath(packageName))
344
+ };
345
+ } catch {
346
+ // Workspace builds expose bundled @circuitwall packages from the app root.
347
+ }
348
+ if (process.env.JARELA_PACKAGES_DIR?.trim()) return null;
349
+ if (!packageName.startsWith("@circuitwall/")) return null;
350
+ const appReq = createRuntimeRequire((0,node_path__WEBPACK_IMPORTED_MODULE_0__.join)(process.cwd(), "package.json"));
351
+ try {
352
+ return {
353
+ req: appReq,
354
+ path: appReq.resolve(packageName)
355
+ };
356
+ } catch {
357
+ return null;
358
+ }
359
+ }
338
360
  function loadOptionalPackage(packageName) {
339
361
  const override = testOverrides.get(packageName);
340
362
  if (override) return override;
363
+ const resolved = resolveOptionalPackage(packageName);
364
+ if (!resolved) return null;
341
365
  try {
342
- const req = createRuntimeRequire((0,node_path__WEBPACK_IMPORTED_MODULE_0__.join)((0,_langchain_packages__WEBPACK_IMPORTED_MODULE_1__/* .getPackagesDir */ .V_)(), "_anchor"));
343
- return req(managedPackagePath(packageName));
366
+ return resolved.req(resolved.path);
344
367
  } catch (err) {
345
368
  // Keep the optional boundary non-fatal, but leave the actual load failure
346
369
  // visible so missing peer dependencies are diagnosable from server logs.
@@ -372,9 +395,10 @@ function optionalFunction(packageName, exportName, fallback) {
372
395
  };
373
396
  }
374
397
  function isOptionalPackageInstalled(packageName) {
398
+ const resolved = resolveOptionalPackage(packageName);
399
+ if (!resolved) return false;
375
400
  try {
376
- const req = createRuntimeRequire((0,node_path__WEBPACK_IMPORTED_MODULE_0__.join)((0,_langchain_packages__WEBPACK_IMPORTED_MODULE_1__/* .getPackagesDir */ .V_)(), "_anchor"));
377
- req(managedPackagePath(packageName));
401
+ resolved.req(resolved.path);
378
402
  return true;
379
403
  } catch (err) {
380
404
  console.error(`[optional-package] failed to load ${packageName}: ${err instanceof Error ? err.message : String(err)}`);
@@ -202,58 +202,79 @@ function containsText(source, needle, ignoreCase) {
202
202
  if (!ignoreCase) return source.includes(needle);
203
203
  return source.toLowerCase().includes(needle.toLowerCase());
204
204
  }
205
- function dedupeLines(source, opts) {
206
- const lines = source.split(/\r?\n/);
207
- const keyed = lines.map((line)=>{
208
- const normalized = opts.trim ? line.trim() : line;
209
- return opts.ignoreCase ? normalized.toLowerCase() : normalized;
210
- });
211
- const keep = new Set();
212
- const visit = opts.keep === "first" ? [
213
- ...keyed.keys()
214
- ] : [
215
- ...keyed.keys()
205
+ const FENCE_RE = /^\s{0,3}(`{3,}|~{3,})/;
206
+ const HORIZONTAL_RULE_RE = /^\s{0,3}([-*_])(?:\s*\1){2,}\s*$/;
207
+ /**
208
+ * Drop duplicate segments from `parts`, which alternates content (even
209
+ * indexes) and the original separator that follows it (odd indexes).
210
+ * Separators are preserved verbatim, so only removed segments change the
211
+ * text. `keyOf` returns null for a segment that must always be kept.
212
+ */ function dedupeSegments(parts, keyOf, keep) {
213
+ const contentIdx = parts.map((_, i)=>i).filter((i)=>i % 2 === 0);
214
+ const visit = keep === "first" ? contentIdx : [
215
+ ...contentIdx
216
216
  ].reverse();
217
217
  const seen = new Set();
218
+ const dropped = new Set();
218
219
  for (const idx of visit){
219
- const key = keyed[idx];
220
- if (opts.skipBlank && key.length === 0) {
221
- keep.add(idx);
222
- continue;
223
- }
224
- if (seen.has(key)) continue;
225
- seen.add(key);
226
- keep.add(idx);
220
+ const key = keyOf(idx);
221
+ if (key === null) continue;
222
+ if (seen.has(key)) dropped.add(idx);
223
+ else seen.add(key);
224
+ }
225
+ if (dropped.size === 0) return {
226
+ text: parts.join(""),
227
+ removed: 0
228
+ };
229
+ let text = "";
230
+ let emitted = false;
231
+ for (const idx of contentIdx){
232
+ if (dropped.has(idx)) continue;
233
+ if (emitted && idx > 0) text += parts[idx - 1];
234
+ text += parts[idx];
235
+ emitted = true;
227
236
  }
228
- const next = lines.filter((_, idx)=>keep.has(idx));
229
237
  return {
230
- text: next.join("\n"),
231
- removed: lines.length - next.length
238
+ text,
239
+ removed: dropped.size
232
240
  };
233
241
  }
242
+ function dedupeLines(source, opts) {
243
+ const parts = source.split(/(\r?\n)/);
244
+ // Fence markers, fenced code and horizontal rules repeat by design; a
245
+ // closing ``` is always a "duplicate" of the opening one.
246
+ const protectedIdx = new Set();
247
+ let fence = null;
248
+ for(let i = 0; i < parts.length; i += 2){
249
+ const marker = FENCE_RE.exec(parts[i])?.[1];
250
+ if (fence !== null) {
251
+ protectedIdx.add(i);
252
+ if (marker && marker[0] === fence[0] && marker.length >= fence.length && /^\s*[`~]+\s*$/.test(parts[i])) fence = null;
253
+ } else if (marker) {
254
+ protectedIdx.add(i);
255
+ fence = marker;
256
+ } else if (HORIZONTAL_RULE_RE.test(parts[i])) {
257
+ protectedIdx.add(i);
258
+ }
259
+ }
260
+ return dedupeSegments(parts, (i)=>{
261
+ if (protectedIdx.has(i)) return null;
262
+ const normalized = opts.trim ? parts[i].trim() : parts[i];
263
+ if (opts.skipBlank && normalized.length === 0) return null;
264
+ return opts.ignoreCase ? normalized.toLowerCase() : normalized;
265
+ }, opts.keep);
266
+ }
234
267
  function dedupeParagraphs(source, opts) {
235
- const paragraphs = source.split(/\n\s*\n/).map((p)=>p.trim()).filter((p)=>p.length > 0);
236
- const keys = paragraphs.map((p)=>{
237
- const base = opts.trim ? p.trim() : p;
268
+ const tail = /\s*$/.exec(source)?.[0] ?? "";
269
+ const parts = source.slice(0, source.length - tail.length).split(/(\r?\n(?:[ \t]*\r?\n)+)/);
270
+ const out = dedupeSegments(parts, (i)=>{
271
+ if (parts[i].trim().length === 0) return null;
272
+ const base = opts.trim ? parts[i].trim() : parts[i];
238
273
  return opts.ignoreCase ? base.toLowerCase() : base;
239
- });
240
- const keep = new Set();
241
- const visit = opts.keep === "first" ? [
242
- ...keys.keys()
243
- ] : [
244
- ...keys.keys()
245
- ].reverse();
246
- const seen = new Set();
247
- for (const idx of visit){
248
- const key = keys[idx];
249
- if (seen.has(key)) continue;
250
- seen.add(key);
251
- keep.add(idx);
252
- }
253
- const next = paragraphs.filter((_, idx)=>keep.has(idx));
274
+ }, opts.keep);
254
275
  return {
255
- text: next.join("\n\n"),
256
- removed: paragraphs.length - next.length
276
+ text: out.text + tail,
277
+ removed: out.removed
257
278
  };
258
279
  }
259
280