@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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@circuitwall/jarela",
3
- "version": "2.3.0",
3
+ "version": "2.5.0",
4
4
  "description": "Jarela — local chat interface for LangGraph agents (multi-provider, single-process, SQLite-backed).",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Andrew Ge Wu",
package/CHANGELOG.md CHANGED
@@ -7,6 +7,58 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.5.0] - 2026-10-18
11
+
12
+ ### Added
13
+
14
+ - Add appearance presets and accent-color choices, and reorganize configuration
15
+ panels for a more consistent workspace.
16
+
17
+ ### Fixed
18
+
19
+ - Let assistant replies use the available chat width and keep long tool names,
20
+ status indicators, expanded tool details, and the composer within narrow screens.
21
+ - Align configuration panel widths on desktop and mobile.
22
+ - Refresh provider model catalogs and capability metadata.
23
+ - Resolve optional tool packages from the workspace when their package entry
24
+ point is unavailable.
25
+
26
+ ### Changed
27
+
28
+ - Reuse short-lived startup reads between the agent picker and chat so the
29
+ first agent load warms profile, configuration, and conversation data without
30
+ duplicate requests. Writes and run submissions invalidate the handoff.
31
+ - Speed up chat pagination, dashboard date windows, and thread lists with
32
+ automatically created SQLite indexes. The first startup after upgrading a
33
+ large database may take longer while indexes build.
34
+ - Index latest usage, memory ordering, signal cleanup, background retention,
35
+ and expired leases; find the next embedding wakeup with indexed lookups
36
+ instead of scanning the active queue.
37
+ - Reduce chat refresh work with metadata-only, visibility-aware polling and
38
+ reuse historical message rows during streaming updates.
39
+ - Reuse unchanged dashboard computations briefly, avoid loading snapshotted
40
+ message bodies for token estimates, and skip rereading unchanged pricing
41
+ snapshots. Database writes and pricing changes invalidate server-side metrics.
42
+
43
+ ## [2.4.0] - 2026-10-16
44
+
45
+ ### Added
46
+
47
+ - **Link answers back to the source: Gmail, Outlook mail and calendar, and Google Calendar results carry a web link and a ready-made `cite_as` markdown link, and WhatsApp 1:1 bridge messages carry a `wa.me` chat link, so agents can point you at the exact email, event or chat. Group chats and single WhatsApp messages have no public link. Replies delivered to WhatsApp use bare URLs.**
48
+
49
+ ### Fixed
50
+
51
+ - **Stop bridge-heavy threads from showing "Compacting" and running an empty compaction on every turn, and stop topic detection from re-running every turn after a hot-turn-limit pass.**
52
+ - **Always send localhost traffic (Ollama, the WhatsApp bridge, Jarela's own API) directly when an HTTP proxy is configured, accept IPv6 proxy hosts, and reject manual proxy hosts that include a scheme, port, path or credentials.**
53
+ - **Replay each WhatsApp chat's offline backlog separately so one active chat no longer causes others to lose messages, and keep known chat names when a contact sends a blank name.**
54
+ - **Keep tuned persona sliders when an approved proposal or tool edit updates an agent, apply `hot_turn_limit` from proposals, reject negative or fractional limits, and drop blank or duplicate tool names.**
55
+ - **Save facts about non-Latin subjects under their own memory keys instead of colliding, and stop replacing a fact from deleting an unrelated one.**
56
+ - **Keep distinct Outlook and Gmail links as separate citations, keep closing code fences and horizontal rules when deduplicating instruction lines, and fix fuzzy edits on CRLF files and paged reads that split multi-byte characters.**
57
+
58
+ ### Adoption
59
+
60
+ - If an agent's instructions or skills tell it not to include links or URLs in replies, decide whether it should now cite sources with the provided `cite_as` and chat links.
61
+ - If you listed `localhost` or `127.0.0.1` in the proxy bypass list only to reach local services, you can remove them; loopback is always direct now.
10
62
  ## [2.3.0] - 2026-10-16
11
63
 
12
64
  ### Added
@@ -77,4 +77,14 @@ describe("api request HTTP cache mode", () => {
77
77
 
78
78
  expect((fetchMock.mock.calls[0][1] as RequestInit).cache).toBe("default");
79
79
  });
80
+
81
+ it("fetches live thread metadata without loading a message page", async () => {
82
+ const fetchMock = vi.fn().mockResolvedValue(jsonResponse({ thread_id: "t1" }));
83
+ vi.stubGlobal("fetch", fetchMock);
84
+
85
+ await api.threads.metadata("t1");
86
+
87
+ expect(fetchMock.mock.calls[0][0]).toBe("/api/v1/threads/t1?view=metadata");
88
+ expect((fetchMock.mock.calls[0][1] as RequestInit).cache).toBe("no-store");
89
+ });
80
90
  });
@@ -0,0 +1,129 @@
1
+ // @vitest-environment jsdom
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
+
4
+ vi.mock("./runtime-config", () => ({
5
+ runtimeConfig: () => ({ httpMaxAttempts: 1, httpRequestTimeoutMs: 30_000 }),
6
+ }));
7
+
8
+ function json(body: unknown): Response {
9
+ return new Response(JSON.stringify(body), { headers: { "Content-Type": "application/json" } });
10
+ }
11
+
12
+ describe("agent startup handoff", () => {
13
+ beforeEach(() => {
14
+ vi.resetModules();
15
+ });
16
+ afterEach(() => {
17
+ vi.restoreAllMocks();
18
+ vi.unstubAllGlobals();
19
+ });
20
+
21
+ it("shares all four startup reads between boot and chat without sharing mutable objects", async () => {
22
+ const { api } = await import("./client");
23
+ const fetch = vi.fn().mockImplementation(() => Promise.resolve(json({ nested: { value: "original" } })));
24
+ vi.stubGlobal("fetch", fetch);
25
+ const load = () => Promise.all([
26
+ api.profile.get({ startup: true }),
27
+ api.agents.get("agent", { startup: true }),
28
+ api.agents.getThread("agent", { startup: true }),
29
+ api.threads.get("thread", { startup: true }),
30
+ ]);
31
+ const [boot, chat] = await Promise.all([load(), load()]);
32
+ expect(fetch).toHaveBeenCalledTimes(4);
33
+ expect(chat).toEqual(boot);
34
+ expect(chat[0]).not.toBe(boot[0]);
35
+ await load();
36
+ expect(fetch).toHaveBeenCalledTimes(4);
37
+ for (const [, init] of fetch.mock.calls) {
38
+ expect(init.cache).toBe("no-store");
39
+ expect(init).not.toHaveProperty("startup");
40
+ }
41
+ });
42
+
43
+ it("expires startup reads after five seconds and keeps ordinary reads fresh", async () => {
44
+ const { api } = await import("./client");
45
+ let now = 1_000;
46
+ vi.spyOn(Date, "now").mockImplementation(() => now);
47
+ const fetch = vi.fn().mockImplementation(() => Promise.resolve(json({ name: "profile" })));
48
+ vi.stubGlobal("fetch", fetch);
49
+ await api.profile.get({ startup: true });
50
+ now += 4_999;
51
+ await api.profile.get({ startup: true });
52
+ expect(fetch).toHaveBeenCalledTimes(1);
53
+ now += 1;
54
+ await api.profile.get({ startup: true });
55
+ expect(fetch).toHaveBeenCalledTimes(2);
56
+ await api.profile.get();
57
+ await api.profile.get({ startup: true });
58
+ expect(fetch).toHaveBeenCalledTimes(4);
59
+ });
60
+
61
+ it("isolates nested transcript data between consumers", async () => {
62
+ const { api } = await import("./client");
63
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue(json({ messages: [{ id: "message" }] })));
64
+ const first = await api.threads.get("thread", { startup: true });
65
+ first.messages.length = 0;
66
+ const second = await api.threads.get("thread", { startup: true });
67
+ expect(second.messages).toEqual([{ id: "message" }]);
68
+ });
69
+
70
+ it("invalidates pending warmups before writes and never caches failed reads", async () => {
71
+ const { api } = await import("./client");
72
+ let resolveOld!: (response: Response) => void;
73
+ const fetch = vi.fn()
74
+ .mockImplementationOnce(() => new Promise<Response>((resolve) => { resolveOld = resolve; }))
75
+ .mockResolvedValueOnce(json({ name: "updated" }))
76
+ .mockResolvedValueOnce(json({ name: "updated" }))
77
+ .mockResolvedValueOnce(new Response("failed", { status: 400 }))
78
+ .mockResolvedValueOnce(json({ name: "recovered" }));
79
+ vi.stubGlobal("fetch", fetch);
80
+ const old = api.profile.get({ startup: true });
81
+ await api.profile.update({ name: "updated" });
82
+ resolveOld(json({ name: "old" }));
83
+ await old;
84
+ expect(await api.profile.get({ startup: true })).toEqual({ name: "updated" });
85
+ await expect(api.agents.get("retry", { startup: true })).rejects.toThrow("400");
86
+ await expect(api.agents.get("retry", { startup: true })).resolves.toEqual({ name: "recovered" });
87
+ expect(fetch).toHaveBeenCalledTimes(5);
88
+ });
89
+
90
+ it("clears the startup page before submitting a run", async () => {
91
+ const { api, submitRun } = await import("./client");
92
+ const fetch = vi.fn()
93
+ .mockResolvedValueOnce(json({ messages: [] }))
94
+ .mockResolvedValueOnce(new Response(null, { status: 202 }))
95
+ .mockResolvedValueOnce(json({ messages: [{ id: "new" }] }));
96
+ vi.stubGlobal("fetch", fetch);
97
+ await api.threads.get("thread", { startup: true });
98
+ await submitRun("thread", "hello", new AbortController().signal);
99
+ await expect(api.threads.get("thread", { startup: true })).resolves.toEqual({ messages: [{ id: "new" }] });
100
+ expect(fetch).toHaveBeenCalledTimes(3);
101
+ });
102
+
103
+ it("discards startup data read while a write was still in flight", async () => {
104
+ const { api } = await import("./client");
105
+ let finishWrite!: (response: Response) => void;
106
+ const fetch = vi.fn()
107
+ .mockImplementationOnce(() => new Promise<Response>((resolve) => { finishWrite = resolve; }))
108
+ .mockResolvedValueOnce(json({ name: "old" }))
109
+ .mockResolvedValueOnce(json({ name: "new" }));
110
+ vi.stubGlobal("fetch", fetch);
111
+ const write = api.profile.update({ name: "new" });
112
+ await expect(api.profile.get({ startup: true })).resolves.toEqual({ name: "old" });
113
+ finishWrite(json({ name: "new" }));
114
+ await write;
115
+ await expect(api.profile.get({ startup: true })).resolves.toEqual({ name: "new" });
116
+ expect(fetch).toHaveBeenCalledTimes(3);
117
+ });
118
+
119
+ it("bounds retained data and separates thread cursor requests", async () => {
120
+ const { api } = await import("./client");
121
+ const fetch = vi.fn().mockImplementation(() => Promise.resolve(json({ messages: [] })));
122
+ vi.stubGlobal("fetch", fetch);
123
+ for (let i = 0; i < 17; i++) await api.threads.get(`thread-${i}`, { startup: true });
124
+ await api.threads.get("thread-0", { startup: true });
125
+ expect(fetch).toHaveBeenCalledTimes(18);
126
+ await api.threads.get("thread-0", { after: 10 });
127
+ expect(fetch.mock.calls[18][0]).toBe("/api/v1/threads/thread-0?after=10");
128
+ });
129
+ });
package/api/client.ts CHANGED
@@ -61,6 +61,7 @@ import type {
61
61
  ToolConfigSlotInfo,
62
62
  TaskAssignment,
63
63
  ThreadDetail,
64
+ ThreadMetadata,
64
65
  ThreadSummary,
65
66
  UserProfile,
66
67
  BuiltinToolCategoryInfo,
@@ -369,11 +370,41 @@ async function parseJsonResponse<T>(res: Response): Promise<T> {
369
370
  return JSON.parse(await new Response(stream).text()) as T;
370
371
  }
371
372
 
372
- async function request<T>(path: string, init?: RequestInit & { timeoutMs?: number }): Promise<T> {
373
+ const STARTUP_TTL_MS = 5_000;
374
+ const startupReads = new Map<string, { promise: Promise<unknown>; settledAt: number | null }>();
375
+
376
+ function startupRead<T>(path: string): Promise<T> {
377
+ const now = Date.now();
378
+ for (const [key, entry] of startupReads) {
379
+ if (entry.settledAt !== null && (now < entry.settledAt || now - entry.settledAt >= STARTUP_TTL_MS)) {
380
+ startupReads.delete(key);
381
+ }
382
+ }
383
+ const cached = startupReads.get(path);
384
+ if (cached) return cached.promise.then((value) => structuredClone(value as T));
385
+ // Bound retained startup data when agents are switched rapidly.
386
+ const oldestKey = startupReads.keys().next().value;
387
+ if (startupReads.size >= 16 && oldestKey !== undefined) startupReads.delete(oldestKey);
388
+ const entry = { promise: Promise.resolve<unknown>(undefined), settledAt: null as number | null };
389
+ entry.promise = request<T>(path, { startup: true }).then((value) => {
390
+ entry.settledAt = Date.now();
391
+ return value;
392
+ }).catch((error: unknown) => {
393
+ if (startupReads.get(path) === entry) startupReads.delete(path);
394
+ throw error;
395
+ });
396
+ startupReads.set(path, entry);
397
+ return entry.promise.then((value) => structuredClone(value as T));
398
+ }
399
+
400
+ async function request<T>(path: string, init?: RequestInit & { timeoutMs?: number; startup?: boolean }): Promise<T> {
401
+ const mutation = !!init?.method && init.method !== "GET";
402
+ if (mutation) startupReads.clear();
403
+ else if (!init?.startup) startupReads.delete(path);
373
404
  const cfg = runtimeConfig();
374
405
  const maxAttempts = Math.max(1, cfg.httpMaxAttempts);
375
406
  const timeoutMs = init?.timeoutMs ?? cfg.httpRequestTimeoutMs;
376
- const { timeoutMs: _ignoreTimeoutMs, ...fetchInit } = init ?? {};
407
+ const { timeoutMs: _ignoreTimeoutMs, startup: _ignoreStartup, ...fetchInit } = init ?? {};
377
408
  const callerSignal = fetchInit.signal ?? null;
378
409
 
379
410
  let lastErr: unknown = null;
@@ -434,6 +465,7 @@ async function request<T>(path: string, init?: RequestInit & { timeoutMs?: numbe
434
465
  if (attempt < maxAttempts && isRetryable(err)) continue;
435
466
  throw err;
436
467
  } finally {
468
+ if (mutation) startupReads.clear();
437
469
  clearTimeout(timer);
438
470
  callerSignal?.removeEventListener("abort", onCallerAbort);
439
471
  }
@@ -446,7 +478,10 @@ export const api = {
446
478
  agents: {
447
479
  list: (opts?: { force?: boolean }) =>
448
480
  cachedList(agentListCache, () => request<AgentConfig[]>("/agents"), setAgentListCache, opts?.force === true),
449
- get: (id: string) => request<AgentConfig>(`/agents/${encodeURIComponent(id)}`),
481
+ get: (id: string, opts?: { startup?: boolean }) => {
482
+ const path = `/agents/${encodeURIComponent(id)}`;
483
+ return opts?.startup ? startupRead<AgentConfig>(path) : request<AgentConfig>(path);
484
+ },
450
485
  create: async (data: AgentConfigIn) => {
451
486
  const created = await request<AgentConfig>("/agents", { method: "POST", body: JSON.stringify(data) });
452
487
  if (agentListCache.data) setAgentListCache([...agentListCache.data, created]);
@@ -467,8 +502,10 @@ export const api = {
467
502
  }
468
503
  return res;
469
504
  },
470
- getThread: (id: string) =>
471
- request<ThreadSummary>(`/agents/${encodeURIComponent(id)}/thread`),
505
+ getThread: (id: string, opts?: { startup?: boolean }) => {
506
+ const path = `/agents/${encodeURIComponent(id)}/thread`;
507
+ return opts?.startup ? startupRead<ThreadSummary>(path) : request<ThreadSummary>(path);
508
+ },
472
509
  compact: (id: string, options?: { resetContext?: boolean }) =>
473
510
  request<{
474
511
  compacted: boolean;
@@ -705,7 +742,7 @@ export const api = {
705
742
  list: (limit = 50, offset = 0) => request<ThreadSummary[]>(`/threads?limit=${limit}&offset=${offset}`),
706
743
  create: (agent_id: string, title?: string) =>
707
744
  request<ThreadSummary>("/threads", { method: "POST", body: JSON.stringify({ agent_id, title }) }),
708
- get: (thread_id: string, opts?: { limit?: number; before?: number; after?: number }) => {
745
+ get: (thread_id: string, opts?: { limit?: number; before?: number; after?: number; startup?: boolean }) => {
709
746
  const p = new URLSearchParams();
710
747
  if (opts?.limit) p.set("limit", String(opts.limit));
711
748
  if (opts?.before !== undefined) p.set("before", String(opts.before));
@@ -714,8 +751,11 @@ export const api = {
714
751
  // user+assistant pair instead of re-pulling the whole most-recent page.
715
752
  if (opts?.after !== undefined) p.set("after", String(opts.after));
716
753
  const qs = p.toString() ? `?${p}` : "";
717
- return request<ThreadDetail>(`/threads/${thread_id}${qs}`);
754
+ const path = `/threads/${thread_id}${qs}`;
755
+ return opts?.startup ? startupRead<ThreadDetail>(path) : request<ThreadDetail>(path);
718
756
  },
757
+ metadata: (thread_id: string, signal?: AbortSignal) =>
758
+ request<ThreadMetadata>(`/threads/${thread_id}?view=metadata`, { signal }),
719
759
  delete: (thread_id: string) =>
720
760
  request<{ deleted: boolean }>(`/threads/${thread_id}`, { method: "DELETE" }),
721
761
  // What the thread is doing now (run phase, queued turns, compaction).
@@ -893,7 +933,8 @@ export const api = {
893
933
  },
894
934
 
895
935
  profile: {
896
- get: () => request<UserProfile>("/profile"),
936
+ get: (opts?: { startup?: boolean }) =>
937
+ opts?.startup ? startupRead<UserProfile>("/profile") : request<UserProfile>("/profile"),
897
938
  update: (data: Partial<Pick<UserProfile, "name" | "icon" | "about" | "preset">>) =>
898
939
  request<UserProfile>("/profile", { method: "PUT", body: JSON.stringify(data) }),
899
940
  setLocationConsent: (consent: boolean) =>
@@ -1397,6 +1438,7 @@ export async function submitRun(
1397
1438
  channels?: string[],
1398
1439
  hot_since_seq?: number | null,
1399
1440
  ): Promise<SubmitResult> {
1441
+ startupReads.clear();
1400
1442
  const payload: {
1401
1443
  message: string;
1402
1444
  stream_options?: StreamOptions;
@@ -1413,7 +1455,7 @@ export async function submitRun(
1413
1455
  headers: { "Content-Type": "application/json" },
1414
1456
  body: JSON.stringify(runPayload),
1415
1457
  signal,
1416
- });
1458
+ }).finally(() => startupReads.clear());
1417
1459
  // 2xx = accepted (currently always 202); 409 = already running. We treat
1418
1460
  // every other status as a hard error so the consumer's catch fires.
1419
1461
  if (res.status === 202) {
@@ -0,0 +1,72 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { NextRequest } from "next/server";
3
+
4
+ const mocks = vi.hoisted(() => ({
5
+ getThread: vi.fn(), getMessagesPage: vi.fn(), getMessagesAfter: vi.fn(),
6
+ getMessageUsageByIds: vi.fn(), getAgentConfig: vi.fn(),
7
+ }));
8
+ vi.mock("@/lib/stores/threads", () => ({ ...mocks, deleteThread: vi.fn() }));
9
+ vi.mock("@/lib/stores/message-usage", () => ({ getMessageUsageByIds: mocks.getMessageUsageByIds }));
10
+ vi.mock("@/lib/stores/agent-configs", () => ({ getAgentConfig: mocks.getAgentConfig }));
11
+ vi.mock("@/lib/stores/model-config", () => ({ getModelConfig: vi.fn(), getModelParams: vi.fn() }));
12
+ vi.mock("@/lib/agents/warm-summary-background", () => ({
13
+ currentChannelSummaries: () => [], isManualCompactionRunning: () => true,
14
+ pendingCompactionBoundary: () => null, pendingCompactionBoundarySeq: () => 10,
15
+ }));
16
+ vi.mock("@/lib/agents/conversation-summary", () => ({ parseStoredTopics: () => null }));
17
+ vi.mock("@/lib/agents/thread-activity", () => ({ getThreadActivity: () => ({ busy: true }) }));
18
+ vi.mock("@/lib/agents/checkpointer", () => ({ getCheckpointer: vi.fn() }));
19
+
20
+ const { GET } = await import("@/app/api/v1/threads/[thread_id]/route");
21
+ function get(query = "") {
22
+ return GET(new NextRequest(`http://localhost/api/v1/threads/t1${query}`),
23
+ { params: Promise.resolve({ thread_id: "t1" }) });
24
+ }
25
+
26
+ describe("thread metadata view", () => {
27
+ beforeEach(() => {
28
+ vi.clearAllMocks();
29
+ mocks.getThread.mockReturnValue({ thread_id: "t1", agent_id: "a1", hot_since_seq: 4 });
30
+ mocks.getMessagesPage.mockReturnValue({ messages: [], has_more: true });
31
+ mocks.getMessagesAfter.mockReturnValue([]);
32
+ mocks.getMessageUsageByIds.mockReturnValue(new Map());
33
+ mocks.getAgentConfig.mockReturnValue(null);
34
+ });
35
+
36
+ it("returns live metadata without reading messages or usage", async () => {
37
+ const response = await get("?view=metadata");
38
+ const data = await response.json();
39
+ expect(data).toMatchObject({
40
+ thread_id: "t1", hot_since_seq: 4, pending_hot_since_seq: 10,
41
+ manual_compaction_pending: true, activity: { busy: true }, context_window_tokens: 8192,
42
+ });
43
+ expect(data).not.toHaveProperty("messages");
44
+ expect(data).not.toHaveProperty("has_more");
45
+ expect(response.headers.get("Cache-Control")).toBe("no-store");
46
+ expect(mocks.getMessagesPage).not.toHaveBeenCalled();
47
+ expect(mocks.getMessagesAfter).not.toHaveBeenCalled();
48
+ expect(mocks.getMessageUsageByIds).not.toHaveBeenCalled();
49
+ });
50
+
51
+ it("preserves the default paged response and before cursor", async () => {
52
+ const response = await get("?before=42&limit=10");
53
+ expect(await response.json()).toMatchObject({ messages: [], has_more: true });
54
+ expect(mocks.getMessagesPage).toHaveBeenCalledWith("t1", 10, 42);
55
+ expect(mocks.getMessageUsageByIds).toHaveBeenCalledWith([]);
56
+ });
57
+
58
+ it("preserves forward message reads", async () => {
59
+ expect(await (await get("?after=42&limit=10")).json()).toMatchObject({ messages: [], has_more: false });
60
+ expect(mocks.getMessagesAfter).toHaveBeenCalledWith("t1", 42, 10);
61
+ });
62
+
63
+ it("rejects unknown views", async () => {
64
+ expect((await get("?view=unknown")).status).toBe(400);
65
+ expect(mocks.getMessagesPage).not.toHaveBeenCalled();
66
+ });
67
+
68
+ it("preserves missing-thread responses", async () => {
69
+ mocks.getThread.mockReturnValue(null);
70
+ expect((await get("?view=metadata")).status).toBe(404);
71
+ });
72
+ });
package/api/types.ts CHANGED
@@ -378,9 +378,7 @@ export interface MessageUsage {
378
378
  cost_usd: number | null;
379
379
  }
380
380
 
381
- export interface ThreadDetail extends ThreadSummary {
382
- messages: Message[];
383
- has_more: boolean;
381
+ export interface ThreadMetadata extends ThreadSummary {
384
382
  // Effective context-window size (in tokens) for this thread's current
385
383
  // agent + model config. Used by the chat UI to scale the per-turn
386
384
  // context-usage bar against the same cap the agent applies at run time.
@@ -416,6 +414,11 @@ export interface ThreadDetail extends ThreadSummary {
416
414
  warm_channel_summaries?: WarmChannelSummary[];
417
415
  }
418
416
 
417
+ export interface ThreadDetail extends ThreadMetadata {
418
+ messages: Message[];
419
+ has_more: boolean;
420
+ }
421
+
419
422
  /** What a thread is doing right now — GET /threads/:id/activity. */
420
423
  export interface ThreadActivity {
421
424
  run: { phase: string; label: string; started_at: number; agent_id: string | null } | null;