sparkecoder 0.1.98 → 0.1.99

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 (296) hide show
  1. package/dist/agent/index.d.ts +3 -3
  2. package/dist/agent/index.js +1258 -59
  3. package/dist/agent/index.js.map +1 -1
  4. package/dist/cli.js +10659 -8977
  5. package/dist/cli.js.map +1 -1
  6. package/dist/db/index.d.ts +2 -2
  7. package/dist/{index-BvIissiB.d.ts → index-D5l-DMGC.d.ts} +390 -6
  8. package/dist/index.d.ts +5 -5
  9. package/dist/index.js +9985 -8095
  10. package/dist/index.js.map +1 -1
  11. package/dist/{schema-CohdIL13.d.ts → schema-ecQSnCMz.d.ts} +41 -0
  12. package/dist/server/index.js +8852 -6970
  13. package/dist/server/index.js.map +1 -1
  14. package/dist/skills/default/manage-mcp.md +94 -0
  15. package/dist/skills/default/search-conversations.md +100 -0
  16. package/dist/tools/index.d.ts +2 -2
  17. package/dist/tools/index.js +111 -2
  18. package/dist/tools/index.js.map +1 -1
  19. package/package.json +5 -1
  20. package/src/skills/default/manage-mcp.md +94 -0
  21. package/src/skills/default/search-conversations.md +100 -0
  22. package/web/.next/BUILD_ID +1 -1
  23. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  24. package/web/.next/standalone/web/.next/app-path-routes-manifest.json +2 -1
  25. package/web/.next/standalone/web/.next/build-manifest.json +5 -5
  26. package/web/.next/standalone/web/.next/prerender-manifest.json +51 -3
  27. package/web/.next/standalone/web/.next/routes-manifest.json +13 -24
  28. package/web/.next/standalone/web/.next/server/app/(main)/agents/page/app-paths-manifest.json +3 -0
  29. package/web/.next/standalone/web/.next/server/app/{embed/[id] → (main)/agents}/page/build-manifest.json +3 -3
  30. package/web/.next/standalone/web/.next/server/app/{embed/[id] → (main)/agents}/page/next-font-manifest.json +1 -1
  31. package/web/.next/standalone/web/.next/server/app/{embed/[id] → (main)/agents}/page.js +7 -6
  32. package/web/.next/standalone/web/.next/server/app/(main)/agents/page.js.nft.json +1 -0
  33. package/web/.next/standalone/web/.next/server/app/(main)/agents/page_client-reference-manifest.js +2 -0
  34. package/web/.next/standalone/web/.next/server/app/(main)/page/build-manifest.json +3 -3
  35. package/web/.next/standalone/web/.next/server/app/(main)/page.js.nft.json +1 -1
  36. package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
  37. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page/build-manifest.json +3 -3
  38. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
  39. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
  40. package/web/.next/standalone/web/.next/server/app/(main)/settings/page/app-paths-manifest.json +3 -0
  41. package/web/.next/standalone/web/.next/server/app/(main)/settings/page/build-manifest.json +18 -0
  42. package/web/.next/standalone/web/.next/server/app/(main)/settings/page/next-font-manifest.json +11 -0
  43. package/web/.next/standalone/web/.next/server/app/(main)/settings/page/react-loadable-manifest.json +1 -0
  44. package/web/.next/standalone/web/.next/server/app/(main)/settings/page/server-reference-manifest.json +4 -0
  45. package/web/.next/standalone/web/.next/server/app/(main)/settings/page.js +21 -0
  46. package/web/.next/standalone/web/.next/server/app/(main)/settings/page.js.map +5 -0
  47. package/web/.next/standalone/web/.next/server/app/(main)/settings/page.js.nft.json +1 -0
  48. package/web/.next/standalone/web/.next/server/app/(main)/settings/page_client-reference-manifest.js +2 -0
  49. package/web/.next/standalone/web/.next/server/app/_global-error/page/build-manifest.json +3 -3
  50. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  51. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  52. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  53. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  54. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  55. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  56. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  57. package/web/.next/standalone/web/.next/server/app/_not-found/page/build-manifest.json +3 -3
  58. package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  59. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  60. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
  61. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  62. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  63. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  64. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  65. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  66. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  67. package/web/.next/standalone/web/.next/server/app/agents.html +1 -0
  68. package/web/.next/standalone/web/.next/server/app/agents.meta +16 -0
  69. package/web/.next/standalone/web/.next/server/app/agents.rsc +25 -0
  70. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents/__PAGE__.segment.rsc +9 -0
  71. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents.segment.rsc +4 -0
  72. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p.segment.rsc +7 -0
  73. package/web/.next/standalone/web/.next/server/app/agents.segments/_full.segment.rsc +25 -0
  74. package/web/.next/standalone/web/.next/server/app/agents.segments/_head.segment.rsc +6 -0
  75. package/web/.next/standalone/web/.next/server/app/agents.segments/_index.segment.rsc +7 -0
  76. package/web/.next/standalone/web/.next/server/app/agents.segments/_tree.segment.rsc +5 -0
  77. package/web/.next/standalone/web/.next/server/app/api/config/route.js.nft.json +1 -1
  78. package/web/.next/standalone/web/.next/server/app/api/health/route.js.nft.json +1 -1
  79. package/web/.next/standalone/web/.next/server/app/docs/installation/page/build-manifest.json +3 -3
  80. package/web/.next/standalone/web/.next/server/app/docs/installation/page_client-reference-manifest.js +1 -1
  81. package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
  82. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +2 -2
  83. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +2 -2
  84. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
  85. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +2 -2
  86. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +2 -2
  87. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
  88. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
  89. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
  90. package/web/.next/standalone/web/.next/server/app/docs/page/build-manifest.json +3 -3
  91. package/web/.next/standalone/web/.next/server/app/docs/page_client-reference-manifest.js +1 -1
  92. package/web/.next/standalone/web/.next/server/app/docs/skills/page/build-manifest.json +3 -3
  93. package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +1 -1
  94. package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
  95. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +2 -2
  96. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +2 -2
  97. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
  98. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +2 -2
  99. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +2 -2
  100. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
  101. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
  102. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
  103. package/web/.next/standalone/web/.next/server/app/docs/tools/page/build-manifest.json +3 -3
  104. package/web/.next/standalone/web/.next/server/app/docs/tools/page_client-reference-manifest.js +1 -1
  105. package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
  106. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +2 -2
  107. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +2 -2
  108. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
  109. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +2 -2
  110. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +2 -2
  111. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
  112. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
  113. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
  114. package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
  115. package/web/.next/standalone/web/.next/server/app/docs.rsc +2 -2
  116. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +2 -2
  117. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
  118. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +2 -2
  119. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +2 -2
  120. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
  121. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
  122. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  123. package/web/.next/standalone/web/.next/server/app/index.rsc +4 -4
  124. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +2 -2
  125. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +2 -2
  126. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +4 -4
  127. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  128. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
  129. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  130. package/web/.next/standalone/web/.next/server/app/settings.html +1 -0
  131. package/web/.next/standalone/web/.next/server/app/settings.meta +16 -0
  132. package/web/.next/standalone/web/.next/server/app/settings.rsc +25 -0
  133. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings/__PAGE__.segment.rsc +9 -0
  134. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings.segment.rsc +4 -0
  135. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p.segment.rsc +7 -0
  136. package/web/.next/standalone/web/.next/server/app/settings.segments/_full.segment.rsc +25 -0
  137. package/web/.next/standalone/web/.next/server/app/settings.segments/_head.segment.rsc +6 -0
  138. package/web/.next/standalone/web/.next/server/app/settings.segments/_index.segment.rsc +7 -0
  139. package/web/.next/standalone/web/.next/server/app/settings.segments/_tree.segment.rsc +5 -0
  140. package/web/.next/standalone/web/.next/server/app-paths-manifest.json +2 -1
  141. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_60d8842c._.js → 2374f_3b04c7b5._.js} +1 -1
  142. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_2801b766._.js → 2374f_5ee1ee50._.js} +1 -1
  143. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_c13c8f4f._.js → 2374f_7b7dd4c7._.js} +1 -1
  144. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_b7f45fdf._.js → 2374f_9e444fb0._.js} +1 -1
  145. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_a383a4d9._.js → 2374f_b57914d2._.js} +1 -1
  146. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_c2c47039._.js +3 -0
  147. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_806bd012._.js → 2374f_db1d6704._.js} +1 -1
  148. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_f363c084._.js → 2374f_e6dbbf5d._.js} +1 -1
  149. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_lucide-react_dist_esm_icons_50c2f239._.js +3 -0
  150. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__2ea52390._.js +3 -0
  151. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__6097da17._.js +15 -0
  152. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__9f149e88._.js +3 -0
  153. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__b050bb8f._.js +1 -1
  154. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__c94db61a._.js +3 -0
  155. package/web/.next/standalone/web/.next/server/chunks/ssr/web_4fe3c244._.js +3 -0
  156. package/web/.next/standalone/web/.next/server/chunks/ssr/{web_3c2b112b._.js → web_7ca56356._.js} +3 -3
  157. package/web/.next/standalone/web/.next/server/chunks/ssr/web_8e76ee8b._.js +8 -0
  158. package/web/.next/standalone/web/.next/server/chunks/ssr/web_90d4125e._.js +7 -0
  159. package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_(main)_agents_page_actions_30f6e448.js +3 -0
  160. package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_(main)_settings_page_actions_7285839d.js +3 -0
  161. package/web/.next/standalone/web/.next/server/chunks/ssr/web_b38a47ee._.js +4 -0
  162. package/web/.next/standalone/web/.next/server/chunks/ssr/web_f7cf6b63._.js +3 -0
  163. package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_app_(main)_layout_tsx_453f6492._.js +3 -0
  164. package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_app_(main)_page_tsx_5ac4794b._.js +1 -1
  165. package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_app_(main)_settings_page_tsx_eb320e07._.js +33 -0
  166. package/web/.next/standalone/web/.next/server/middleware-build-manifest.js +3 -3
  167. package/web/.next/standalone/web/.next/server/next-font-manifest.js +1 -1
  168. package/web/.next/standalone/web/.next/server/next-font-manifest.json +8 -4
  169. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  170. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  171. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  172. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  173. package/web/.next/standalone/web/.next/static/chunks/03b5edce6d5b809e.js +1 -0
  174. package/web/.next/standalone/web/.next/static/chunks/29dcecc3c2ca92b0.js +1 -0
  175. package/web/.next/standalone/web/.next/static/chunks/344be859c2c8600b.css +1 -0
  176. package/web/.next/standalone/web/.next/static/chunks/545725e4c1237026.js +7 -0
  177. package/web/.next/standalone/web/.next/static/chunks/60359bdd369c0c72.js +1 -0
  178. package/web/.next/standalone/web/.next/static/chunks/699803c4fb2dd3fc.js +5 -0
  179. package/web/.next/standalone/web/.next/static/chunks/735a2408c315b2f0.js +1 -0
  180. package/web/.next/standalone/web/.next/static/chunks/d54077a2bb8314ed.js +31 -0
  181. package/web/.next/standalone/web/.next/static/chunks/dc34aa94e57fa28e.js +3 -0
  182. package/web/.next/standalone/web/.next/static/chunks/ea89ca7892d8c557.js +1 -0
  183. package/web/.next/standalone/web/.next/static/chunks/f0f19357f3fb7cf8.js +1 -0
  184. package/web/.next/standalone/web/.next/static/chunks/f50a66c24c564585.js +13 -0
  185. package/web/.next/standalone/web/.next/static/chunks/f5fe518b79d1bf41.js +1 -0
  186. package/web/.next/{static/chunks/turbopack-597558bb7b6982f6.js → standalone/web/.next/static/chunks/turbopack-2c0905c7bbebae3f.js} +1 -1
  187. package/web/.next/standalone/web/.next/static/static/chunks/03b5edce6d5b809e.js +1 -0
  188. package/web/.next/standalone/web/.next/static/static/chunks/29dcecc3c2ca92b0.js +1 -0
  189. package/web/.next/standalone/web/.next/static/static/chunks/344be859c2c8600b.css +1 -0
  190. package/web/.next/standalone/web/.next/static/static/chunks/545725e4c1237026.js +7 -0
  191. package/web/.next/standalone/web/.next/static/static/chunks/60359bdd369c0c72.js +1 -0
  192. package/web/.next/standalone/web/.next/static/static/chunks/699803c4fb2dd3fc.js +5 -0
  193. package/web/.next/standalone/web/.next/static/static/chunks/735a2408c315b2f0.js +1 -0
  194. package/web/.next/standalone/web/.next/static/static/chunks/d54077a2bb8314ed.js +31 -0
  195. package/web/.next/standalone/web/.next/static/static/chunks/dc34aa94e57fa28e.js +3 -0
  196. package/web/.next/standalone/web/.next/static/static/chunks/ea89ca7892d8c557.js +1 -0
  197. package/web/.next/standalone/web/.next/static/static/chunks/f0f19357f3fb7cf8.js +1 -0
  198. package/web/.next/standalone/web/.next/static/static/chunks/f50a66c24c564585.js +13 -0
  199. package/web/.next/standalone/web/.next/static/static/chunks/f5fe518b79d1bf41.js +1 -0
  200. package/web/.next/standalone/web/.next/static/{chunks/turbopack-597558bb7b6982f6.js → static/chunks/turbopack-2c0905c7bbebae3f.js} +1 -1
  201. package/web/.next/standalone/web/next.config.ts +1 -55
  202. package/web/.next/standalone/web/package-lock.json +3 -3
  203. package/web/.next/standalone/web/src/app/(main)/agents/page.tsx +222 -0
  204. package/web/.next/standalone/web/src/app/(main)/page.tsx +40 -1
  205. package/web/.next/standalone/web/src/app/(main)/session/[id]/page.tsx +9 -1
  206. package/web/.next/standalone/web/src/app/(main)/settings/page.tsx +1116 -0
  207. package/web/.next/standalone/web/src/components/chat-interface.tsx +205 -4
  208. package/web/.next/standalone/web/src/components/pending-question-banner.tsx +106 -0
  209. package/web/.next/standalone/web/src/components/sessions-sidebar.tsx +120 -50
  210. package/web/.next/standalone/web/src/lib/api.ts +43 -2
  211. package/web/.next/static/chunks/03b5edce6d5b809e.js +1 -0
  212. package/web/.next/static/chunks/29dcecc3c2ca92b0.js +1 -0
  213. package/web/.next/static/chunks/344be859c2c8600b.css +1 -0
  214. package/web/.next/static/chunks/545725e4c1237026.js +7 -0
  215. package/web/.next/static/chunks/60359bdd369c0c72.js +1 -0
  216. package/web/.next/static/chunks/699803c4fb2dd3fc.js +5 -0
  217. package/web/.next/static/chunks/735a2408c315b2f0.js +1 -0
  218. package/web/.next/static/chunks/d54077a2bb8314ed.js +31 -0
  219. package/web/.next/static/chunks/dc34aa94e57fa28e.js +3 -0
  220. package/web/.next/static/chunks/ea89ca7892d8c557.js +1 -0
  221. package/web/.next/static/chunks/f0f19357f3fb7cf8.js +1 -0
  222. package/web/.next/static/chunks/f50a66c24c564585.js +13 -0
  223. package/web/.next/static/chunks/f5fe518b79d1bf41.js +1 -0
  224. package/web/.next/{standalone/web/.next/static/static/chunks/turbopack-597558bb7b6982f6.js → static/chunks/turbopack-2c0905c7bbebae3f.js} +1 -1
  225. package/web/.next/standalone/web/.next/server/app/embed/[id]/page/app-paths-manifest.json +0 -3
  226. package/web/.next/standalone/web/.next/server/app/embed/[id]/page.js.nft.json +0 -1
  227. package/web/.next/standalone/web/.next/server/app/embed/[id]/page_client-reference-manifest.js +0 -2
  228. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_317b1fef._.js +0 -3
  229. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_37dd9702._.js +0 -45
  230. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_4d44e4ed._.js +0 -26
  231. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_54ac917f._.js +0 -3
  232. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_86585101._.js +0 -3
  233. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_c59a35bb._.js +0 -3
  234. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_fdfc7f3d._.js +0 -31
  235. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__234f92d8._.js +0 -3
  236. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__9a826344._.js +0 -3
  237. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__9d3a7cbf._.js +0 -15
  238. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__de76483d._.js +0 -3
  239. package/web/.next/standalone/web/.next/server/chunks/ssr/web_08242997._.js +0 -3
  240. package/web/.next/standalone/web/.next/server/chunks/ssr/web_123ffe97._.js +0 -8
  241. package/web/.next/standalone/web/.next/server/chunks/ssr/web_5cca707f._.js +0 -7
  242. package/web/.next/standalone/web/.next/server/chunks/ssr/web_935e81f5._.js +0 -7
  243. package/web/.next/standalone/web/.next/server/chunks/ssr/web_99b01335._.js +0 -8
  244. package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_embed_[id]_page_actions_dd0b7fea.js +0 -3
  245. package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_components_sessions-sidebar_tsx_92510070._.js +0 -3
  246. package/web/.next/standalone/web/.next/static/chunks/1ebba7ac024244f9.js +0 -5
  247. package/web/.next/standalone/web/.next/static/chunks/275e8268daf318b2.js +0 -7
  248. package/web/.next/standalone/web/.next/static/chunks/2bf377e04592f3c8.js +0 -13
  249. package/web/.next/standalone/web/.next/static/chunks/376a123113ccf5eb.js +0 -3
  250. package/web/.next/standalone/web/.next/static/chunks/58fd0aaa2746b444.js +0 -1
  251. package/web/.next/standalone/web/.next/static/chunks/61c9922b38a9569d.js +0 -3
  252. package/web/.next/standalone/web/.next/static/chunks/74b64476a24dd71e.css +0 -1
  253. package/web/.next/standalone/web/.next/static/chunks/767bcdfbabf0703e.js +0 -7
  254. package/web/.next/standalone/web/.next/static/chunks/a888d448ceab1abe.js +0 -1
  255. package/web/.next/standalone/web/.next/static/chunks/b9ad1584d4e11d12.js +0 -1
  256. package/web/.next/standalone/web/.next/static/chunks/c6f40df16a9396b9.js +0 -1
  257. package/web/.next/standalone/web/.next/static/chunks/ea29be392100ab0f.js +0 -5
  258. package/web/.next/standalone/web/.next/static/static/chunks/1ebba7ac024244f9.js +0 -5
  259. package/web/.next/standalone/web/.next/static/static/chunks/275e8268daf318b2.js +0 -7
  260. package/web/.next/standalone/web/.next/static/static/chunks/2bf377e04592f3c8.js +0 -13
  261. package/web/.next/standalone/web/.next/static/static/chunks/376a123113ccf5eb.js +0 -3
  262. package/web/.next/standalone/web/.next/static/static/chunks/58fd0aaa2746b444.js +0 -1
  263. package/web/.next/standalone/web/.next/static/static/chunks/61c9922b38a9569d.js +0 -3
  264. package/web/.next/standalone/web/.next/static/static/chunks/74b64476a24dd71e.css +0 -1
  265. package/web/.next/standalone/web/.next/static/static/chunks/767bcdfbabf0703e.js +0 -7
  266. package/web/.next/standalone/web/.next/static/static/chunks/a888d448ceab1abe.js +0 -1
  267. package/web/.next/standalone/web/.next/static/static/chunks/b9ad1584d4e11d12.js +0 -1
  268. package/web/.next/standalone/web/.next/static/static/chunks/c6f40df16a9396b9.js +0 -1
  269. package/web/.next/standalone/web/.next/static/static/chunks/ea29be392100ab0f.js +0 -5
  270. package/web/.next/standalone/web/src/app/embed/[id]/page.tsx +0 -77
  271. package/web/.next/standalone/web/src/lib/embed-bootstrap.ts +0 -108
  272. package/web/.next/static/chunks/1ebba7ac024244f9.js +0 -5
  273. package/web/.next/static/chunks/275e8268daf318b2.js +0 -7
  274. package/web/.next/static/chunks/2bf377e04592f3c8.js +0 -13
  275. package/web/.next/static/chunks/376a123113ccf5eb.js +0 -3
  276. package/web/.next/static/chunks/58fd0aaa2746b444.js +0 -1
  277. package/web/.next/static/chunks/61c9922b38a9569d.js +0 -3
  278. package/web/.next/static/chunks/74b64476a24dd71e.css +0 -1
  279. package/web/.next/static/chunks/767bcdfbabf0703e.js +0 -7
  280. package/web/.next/static/chunks/a888d448ceab1abe.js +0 -1
  281. package/web/.next/static/chunks/b9ad1584d4e11d12.js +0 -1
  282. package/web/.next/static/chunks/c6f40df16a9396b9.js +0 -1
  283. package/web/.next/static/chunks/ea29be392100ab0f.js +0 -5
  284. package/dist/{search-CCffrVJE.d.ts → search-DOzC4ojH.d.ts} +1 -1
  285. /package/web/.next/standalone/web/.next/server/app/{embed/[id] → (main)/agents}/page/react-loadable-manifest.json +0 -0
  286. /package/web/.next/standalone/web/.next/server/app/{embed/[id] → (main)/agents}/page/server-reference-manifest.json +0 -0
  287. /package/web/.next/standalone/web/.next/server/app/{embed/[id] → (main)/agents}/page.js.map +0 -0
  288. /package/web/.next/standalone/web/.next/static/{WCqUmRTRCgZqwBVGKQESX → static/yyzMTo2RYusiXE0e1_kdc}/_buildManifest.js +0 -0
  289. /package/web/.next/standalone/web/.next/static/{WCqUmRTRCgZqwBVGKQESX → static/yyzMTo2RYusiXE0e1_kdc}/_clientMiddlewareManifest.json +0 -0
  290. /package/web/.next/standalone/web/.next/static/{WCqUmRTRCgZqwBVGKQESX → static/yyzMTo2RYusiXE0e1_kdc}/_ssgManifest.js +0 -0
  291. /package/web/.next/standalone/web/.next/static/{static/WCqUmRTRCgZqwBVGKQESX → yyzMTo2RYusiXE0e1_kdc}/_buildManifest.js +0 -0
  292. /package/web/.next/standalone/web/.next/static/{static/WCqUmRTRCgZqwBVGKQESX → yyzMTo2RYusiXE0e1_kdc}/_clientMiddlewareManifest.json +0 -0
  293. /package/web/.next/standalone/web/.next/static/{static/WCqUmRTRCgZqwBVGKQESX → yyzMTo2RYusiXE0e1_kdc}/_ssgManifest.js +0 -0
  294. /package/web/.next/static/{WCqUmRTRCgZqwBVGKQESX → yyzMTo2RYusiXE0e1_kdc}/_buildManifest.js +0 -0
  295. /package/web/.next/static/{WCqUmRTRCgZqwBVGKQESX → yyzMTo2RYusiXE0e1_kdc}/_clientMiddlewareManifest.json +0 -0
  296. /package/web/.next/static/{WCqUmRTRCgZqwBVGKQESX → yyzMTo2RYusiXE0e1_kdc}/_ssgManifest.js +0 -0
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
 
3
+ import * as React from 'react';
3
4
  import { useState, useRef, useEffect } from 'react';
4
5
  import Image from 'next/image';
5
6
  import { cn } from '@/lib/utils';
@@ -238,6 +239,167 @@ function stripDevtoolsContext(text: string): string {
238
239
  return text.replace(DEVTOOLS_CONTEXT_RE, '').trim();
239
240
  }
240
241
 
242
+ /** Channel pill at the start of an orchestrator user-message, e.g. "[SLACK #ops by @ryan]" or "[WEB]". */
243
+ interface ChannelPillInfo {
244
+ channel: string; // SLACK | WEB | SYSTEM | SCHEDULE | WEBHOOK
245
+ detail?: string; // remainder of the bracket, e.g. "#ops by @ryan"
246
+ }
247
+ function extractChannelPill(text: string): { pill: ChannelPillInfo | null; rest: string } {
248
+ const m = text.match(/^\[([A-Z]+)(?:\s+([^\]]*))?\]\s*([\s\S]*)$/);
249
+ if (!m) return { pill: null, rest: text };
250
+ const channel = m[1];
251
+ // Only treat as a channel pill if it's a known channel id.
252
+ if (!['WEB', 'SLACK', 'SYSTEM', 'SCHEDULE', 'WEBHOOK'].includes(channel)) {
253
+ return { pill: null, rest: text };
254
+ }
255
+ return { pill: { channel, detail: m[2]?.trim() || undefined }, rest: m[3] };
256
+ }
257
+
258
+ const CHANNEL_PALETTE: Record<string, string> = {
259
+ SLACK: 'bg-purple-500/10 text-purple-600 dark:text-purple-300 border-purple-500/30',
260
+ SYSTEM: 'bg-blue-500/10 text-blue-600 dark:text-blue-300 border-blue-500/30',
261
+ SCHEDULE: 'bg-amber-500/10 text-amber-600 dark:text-amber-300 border-amber-500/30',
262
+ WEBHOOK: 'bg-emerald-500/10 text-emerald-600 dark:text-emerald-300 border-emerald-500/30',
263
+ };
264
+
265
+ function ChannelPill({ channel, detail }: ChannelPillInfo) {
266
+ // Hide [WEB] pills — web is the default surface, showing a pill on
267
+ // every typed message is noise.
268
+ if (channel === 'WEB') return null;
269
+ const tone = CHANNEL_PALETTE[channel] || 'bg-muted text-muted-foreground border-border';
270
+ return (
271
+ <span className={`inline-flex items-center gap-1 text-[10px] font-medium uppercase tracking-wide px-1.5 py-0.5 rounded border mr-2 mb-1 ${tone}`}>
272
+ <span>{channel}</span>
273
+ {detail && <span className="font-normal normal-case opacity-80">{detail}</span>}
274
+ </span>
275
+ );
276
+ }
277
+
278
+ /** Channels that aren't direct human chat — render compactly with click-to-expand. */
279
+ const SYNTHETIC_CHANNELS = new Set(['SYSTEM', 'SCHEDULE', 'WEBHOOK']);
280
+
281
+ /**
282
+ * Inbound human message from a non-web channel (Slack today, others later).
283
+ * Renders the tag ABOVE a normal user bubble so the convo doesn't read like
284
+ * the channel pill is part of the message body.
285
+ */
286
+ function TaggedInboundUserMessage({
287
+ pill, body, attachmentsBlock, devtoolsTrail, footer,
288
+ }: {
289
+ pill: ChannelPillInfo;
290
+ body: string;
291
+ attachmentsBlock?: React.ReactNode;
292
+ devtoolsTrail?: React.ReactNode;
293
+ footer?: React.ReactNode;
294
+ }) {
295
+ const tone = CHANNEL_PALETTE[pill.channel] || 'bg-muted text-muted-foreground border-border';
296
+ return (
297
+ <div className="flex flex-col items-end gap-1 my-2">
298
+ <div className={`inline-flex items-center gap-1.5 text-[10px] font-medium uppercase tracking-wide px-2 py-0.5 rounded-md border self-end ${tone}`}>
299
+ <span>{pill.channel}</span>
300
+ {pill.detail && <span className="font-normal normal-case opacity-80">{pill.detail}</span>}
301
+ </div>
302
+ <Message from="user">
303
+ <MessageContent>
304
+ {attachmentsBlock}
305
+ {body && <p className="whitespace-pre-wrap">{body}</p>}
306
+ {devtoolsTrail}
307
+ </MessageContent>
308
+ {footer}
309
+ </Message>
310
+ </div>
311
+ );
312
+ }
313
+
314
+ /**
315
+ * Renders an outbound `messenger({action:'post', ...})` tool call as if it
316
+ * were a special assistant message: tag on top ("→ SENT VIA SLACK #ops"),
317
+ * the actual text bubble below, and a small status footer. So when a user
318
+ * comes back to the web later they can see exactly what was sent on other
319
+ * channels.
320
+ */
321
+ function SentMessengerCard({
322
+ input, output, status,
323
+ }: {
324
+ input: any;
325
+ output: any;
326
+ status: string;
327
+ }) {
328
+ if (!input || input.action !== 'post') return null;
329
+ const channel = String(input.channel || 'unknown').toUpperCase();
330
+ const tone = CHANNEL_PALETTE[channel] || 'bg-muted text-muted-foreground border-border';
331
+ const to = String(input.to || '');
332
+ const threadTs = input.threadTs ? String(input.threadTs) : null;
333
+ const text = String(input.text || '');
334
+ const ok = output && typeof output === 'object' && (output as any).ok !== false;
335
+ const errorText = output && typeof output === 'object' ? (output as any).error : undefined;
336
+ const pending = status === 'streaming' || status === 'running' || status === 'pending' || status === 'input-available';
337
+ return (
338
+ <div className="flex flex-col items-start gap-1 my-2 max-w-[80%]">
339
+ <div className={`inline-flex items-center gap-1.5 text-[10px] font-medium uppercase tracking-wide px-2 py-0.5 rounded-md border ${tone}`}>
340
+ <span>→ Sent via {channel}</span>
341
+ {to && <span className="font-normal normal-case opacity-80">to {to}</span>}
342
+ {threadTs && <span className="font-normal normal-case opacity-60">thread {threadTs}</span>}
343
+ </div>
344
+ <div className={`rounded-2xl border bg-card/40 px-3 py-2 ${pending ? 'opacity-60' : ''}`}>
345
+ {text ? (
346
+ <p className="text-sm whitespace-pre-wrap">{text}</p>
347
+ ) : (
348
+ <p className="text-xs italic text-muted-foreground">(empty message)</p>
349
+ )}
350
+ </div>
351
+ <div className="text-[10px] text-muted-foreground flex items-center gap-1">
352
+ {pending ? (
353
+ <span className="opacity-70">sending…</span>
354
+ ) : ok ? (
355
+ <span className="text-emerald-500">✓ delivered</span>
356
+ ) : (
357
+ <span className="text-rose-500">✗ failed{errorText ? `: ${errorText}` : ''}</span>
358
+ )}
359
+ </div>
360
+ </div>
361
+ );
362
+ }
363
+
364
+ /**
365
+ * Synthetic event row — a compact one-liner for SYSTEM/SCHEDULE/WEBHOOK
366
+ * messages so the chat doesn't get cluttered with every worker completion,
367
+ * cron tick, or webhook hit. Click to expand the full payload.
368
+ */
369
+ function SyntheticEventMessage({ pill, body }: { pill: ChannelPillInfo; body: string }) {
370
+ const [expanded, setExpanded] = React.useState(false);
371
+ const tone = CHANNEL_PALETTE[pill.channel] || 'bg-muted text-muted-foreground border-border';
372
+ // First non-empty line for the collapsed summary.
373
+ const firstLine = body.split('\n').find((l) => l.trim().length > 0) || '';
374
+ const truncated = firstLine.length > 140 ? firstLine.slice(0, 140) + '…' : firstLine;
375
+ const isMultiline = body.includes('\n') || body.length > 140;
376
+
377
+ return (
378
+ <div className="flex justify-center my-1.5">
379
+ <button
380
+ type="button"
381
+ onClick={() => isMultiline && setExpanded((v) => !v)}
382
+ className={`group max-w-[80%] text-left rounded-md border px-2 py-1 transition-colors ${tone} ${isMultiline ? 'cursor-pointer hover:opacity-90' : 'cursor-default'}`}
383
+ >
384
+ <div className="flex items-center gap-2 text-[10px] uppercase tracking-wide">
385
+ <span className="font-medium">{pill.channel}</span>
386
+ {pill.detail && <span className="font-normal normal-case opacity-80 truncate">{pill.detail}</span>}
387
+ {isMultiline && (
388
+ <ChevronDown
389
+ className={`h-3 w-3 ml-auto shrink-0 transition-transform ${expanded ? 'rotate-180' : ''}`}
390
+ />
391
+ )}
392
+ </div>
393
+ {expanded ? (
394
+ <pre className="mt-1 text-[11px] whitespace-pre-wrap font-mono opacity-90 max-h-[400px] overflow-y-auto">{body}</pre>
395
+ ) : (
396
+ <p className="mt-0.5 text-[11px] opacity-80 truncate">{truncated}</p>
397
+ )}
398
+ </button>
399
+ </div>
400
+ );
401
+ }
402
+
241
403
  /** Parse devtools context metadata from a user message */
242
404
  function parseDevtoolsContext(text: string): DevtoolsContextMeta | null {
243
405
  const match = text.match(/<devtools-context>([\s\S]*?)<\/devtools-context>/);
@@ -2998,12 +3160,31 @@ export function ChatInterface({ session, isEmbed = false }: ChatInterfaceProps)
2998
3160
  })
2999
3161
  .map((item) => {
3000
3162
  if (item.type === 'user-message') {
3163
+ // Channel-aware rendering for orchestrator-bound user messages.
3164
+ // - SYSTEM/SCHEDULE/WEBHOOK → compact collapsible row
3165
+ // - SLACK and other non-web human channels → tag ABOVE the bubble,
3166
+ // clean body inside (so the chat doesn't read like the pill is
3167
+ // part of the message)
3168
+ // - WEB / no pill → normal user bubble (unchanged)
3169
+ const extracted = item.content ? extractChannelPill(item.content) : { pill: null, rest: item.content || '' };
3170
+ if (extracted.pill && SYNTHETIC_CHANNELS.has(extracted.pill.channel)) {
3171
+ return <SyntheticEventMessage key={item.id} pill={extracted.pill} body={extracted.rest} />;
3172
+ }
3173
+ const useTaggedAbove = !!extracted.pill && extracted.pill.channel !== 'WEB';
3174
+
3001
3175
  // Check if there's a checkpoint for this message (meaning we can revert to before it)
3002
3176
  const hasCheckpoint = item.messageSequence !== undefined &&
3003
3177
  checkpoints.some(cp => cp.messageSequence === item.messageSequence);
3004
3178
 
3005
3179
  return (
3006
- <Message key={item.id} from="user">
3180
+ <div key={item.id} className={useTaggedAbove ? 'flex flex-col items-end gap-1' : undefined}>
3181
+ {useTaggedAbove && extracted.pill && (
3182
+ <div className={`inline-flex items-center gap-1.5 text-[10px] font-medium uppercase tracking-wide px-2 py-0.5 rounded-md border self-end ${CHANNEL_PALETTE[extracted.pill.channel] || 'bg-muted text-muted-foreground border-border'}`}>
3183
+ <span>{extracted.pill.channel}</span>
3184
+ {extracted.pill.detail && <span className="font-normal normal-case opacity-80">{extracted.pill.detail}</span>}
3185
+ </div>
3186
+ )}
3187
+ <Message from="user">
3007
3188
  <MessageContent>
3008
3189
  {/* Display attachments if any */}
3009
3190
  {item.attachments && item.attachments.length > 0 && (
@@ -3032,10 +3213,15 @@ export function ChatInterface({ session, isEmbed = false }: ChatInterfaceProps)
3032
3213
  </div>
3033
3214
  )}
3034
3215
  {item.content && (() => {
3035
- const displayText = stripDevtoolsContext(item.content);
3036
- const devtoolsMeta = parseDevtoolsContext(item.content);
3216
+ // Use the already-extracted pill/rest from the parent
3217
+ // scope so we don't double-render the tag for tagged-above
3218
+ // messages.
3219
+ const rest = extracted.rest;
3220
+ const displayText = stripDevtoolsContext(rest);
3221
+ const devtoolsMeta = parseDevtoolsContext(rest);
3037
3222
  return (
3038
3223
  <>
3224
+ {!useTaggedAbove && extracted.pill && <ChannelPill {...extracted.pill} />}
3039
3225
  {displayText && (
3040
3226
  <p className="whitespace-pre-wrap">{displayText}</p>
3041
3227
  )}
@@ -3078,6 +3264,7 @@ export function ChatInterface({ session, isEmbed = false }: ChatInterfaceProps)
3078
3264
  </MessageActions>
3079
3265
  )}
3080
3266
  </Message>
3267
+ </div>
3081
3268
  );
3082
3269
  }
3083
3270
 
@@ -3126,7 +3313,21 @@ export function ChatInterface({ session, isEmbed = false }: ChatInterfaceProps)
3126
3313
  if (item.type === 'tool-call' && item.toolCall) {
3127
3314
  const tc = item.toolCall;
3128
3315
  const toolType = `tool-${tc.toolName}` as `tool-${string}`;
3129
-
3316
+
3317
+ // Render outbound `messenger({action:'post',...})` as a special
3318
+ // "Sent via SLACK #ops" card so anyone returning to the web can
3319
+ // see what the orchestrator actually pushed to external channels.
3320
+ if (tc.toolName === 'messenger' && (tc.input as any)?.action === 'post') {
3321
+ return (
3322
+ <SentMessengerCard
3323
+ key={item.id}
3324
+ input={tc.input}
3325
+ output={tc.output}
3326
+ status={tc.status}
3327
+ />
3328
+ );
3329
+ }
3330
+
3130
3331
  // Use dedicated WriteFileTool component for write_file
3131
3332
  if (tc.toolName === 'write_file') {
3132
3333
  return (
@@ -0,0 +1,106 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useState, useCallback } from 'react';
4
+ import { MessageCircleQuestion, Loader2 } from 'lucide-react';
5
+ import { Button } from '@/components/ui/button';
6
+ import { getSession, answerAgentQuestion } from '@/lib/api';
7
+
8
+ /**
9
+ * Polls the session and shows a banner inside the chat surface when the
10
+ * worker agent has called `ask_question_to_user`. Lets the user answer
11
+ * inline so they don't have to leave the session.
12
+ */
13
+ export function PendingQuestionBanner({ sessionId }: { sessionId: string }) {
14
+ const [question, setQuestion] = useState<{
15
+ questionId: string;
16
+ question: string;
17
+ context?: string;
18
+ choices?: string[];
19
+ } | null>(null);
20
+ const [answer, setAnswer] = useState('');
21
+ const [sending, setSending] = useState(false);
22
+
23
+ const refresh = useCallback(async () => {
24
+ const s = await getSession(sessionId);
25
+ setQuestion(s?.pendingQuestion ?? null);
26
+ }, [sessionId]);
27
+
28
+ useEffect(() => {
29
+ let cancelled = false;
30
+ const tick = async () => {
31
+ if (cancelled) return;
32
+ await refresh();
33
+ };
34
+ tick();
35
+ const id = setInterval(tick, 3000);
36
+ return () => {
37
+ cancelled = true;
38
+ clearInterval(id);
39
+ };
40
+ }, [refresh]);
41
+
42
+ if (!question) return null;
43
+
44
+ const send = async () => {
45
+ if (!answer.trim() || sending) return;
46
+ setSending(true);
47
+ try {
48
+ await answerAgentQuestion(sessionId, question.questionId, answer.trim());
49
+ setAnswer('');
50
+ setQuestion(null);
51
+ // The worker will resume; the next poll will reflect that.
52
+ refresh();
53
+ } finally {
54
+ setSending(false);
55
+ }
56
+ };
57
+
58
+ return (
59
+ <div className="border-b border-amber-500/40 bg-amber-500/5 px-4 py-3">
60
+ <div className="flex items-start gap-3 max-w-3xl mx-auto">
61
+ <MessageCircleQuestion className="size-5 text-amber-500 shrink-0 mt-0.5" />
62
+ <div className="flex-1 min-w-0">
63
+ <p className="text-sm font-medium text-amber-800 dark:text-amber-200">
64
+ {question.question}
65
+ </p>
66
+ {question.context && (
67
+ <p className="text-xs text-muted-foreground mt-1">{question.context}</p>
68
+ )}
69
+ {question.choices && question.choices.length > 0 && (
70
+ <div className="flex flex-wrap gap-1.5 mt-2">
71
+ {question.choices.map((c) => (
72
+ <button
73
+ key={c}
74
+ type="button"
75
+ onClick={() => setAnswer(c)}
76
+ className="text-xs px-2 py-1 rounded bg-amber-500/10 hover:bg-amber-500/20 text-amber-800 dark:text-amber-200"
77
+ >
78
+ {c}
79
+ </button>
80
+ ))}
81
+ </div>
82
+ )}
83
+ <div className="flex gap-2 mt-2">
84
+ <input
85
+ type="text"
86
+ value={answer}
87
+ onChange={(e) => setAnswer(e.target.value)}
88
+ onKeyDown={(e) => {
89
+ if (e.key === 'Enter') {
90
+ e.preventDefault();
91
+ send();
92
+ }
93
+ }}
94
+ placeholder="Type your answer…"
95
+ className="flex-1 px-3 py-1.5 text-sm rounded border border-border bg-background focus:outline-none focus:ring-2 focus:ring-amber-500/40"
96
+ autoFocus
97
+ />
98
+ <Button size="sm" onClick={send} disabled={!answer.trim() || sending}>
99
+ {sending ? <Loader2 className="size-3.5 animate-spin" /> : 'Answer'}
100
+ </Button>
101
+ </div>
102
+ </div>
103
+ </div>
104
+ </div>
105
+ );
106
+ }
@@ -4,7 +4,7 @@ import { useState, useEffect, useRef } from 'react';
4
4
  import { useRouter, usePathname } from 'next/navigation';
5
5
  import Link from 'next/link';
6
6
  import Image from 'next/image';
7
- import { Plus, MessageSquare, Trash2, Loader2, Settings, PanelLeftClose, PanelLeft, Key, Check, X, Eye, EyeOff, Volume2, ListChecks, ChevronDown } from 'lucide-react';
7
+ import { Plus, MessageSquare, Trash2, Loader2, Settings, PanelLeftClose, PanelLeft, Key, Check, X, Eye, EyeOff, Volume2, ListChecks, ChevronDown, Bot, LayoutGrid, Sliders } from 'lucide-react';
8
8
  import { Badge } from '@/components/ui/badge';
9
9
  import {
10
10
  Sidebar,
@@ -106,18 +106,13 @@ export function SessionsSidebar() {
106
106
  // Notification sound setting
107
107
  const { enabled: soundEnabled, setEnabled: setSoundEnabled } = useNotificationSound();
108
108
 
109
- // Detect if we're in embed mode
110
- const isEmbedMode = pathname.startsWith('/embed/');
111
-
112
109
  // Get current session ID from pathname
113
- const currentSessionId = pathname.startsWith('/session/')
114
- ? pathname.split('/session/')[1]
115
- : pathname.startsWith('/embed/')
116
- ? pathname.split('/embed/')[1]
110
+ const currentSessionId = pathname.startsWith('/session/')
111
+ ? pathname.split('/session/')[1]
117
112
  : null;
118
-
113
+
119
114
  // Base path for session links
120
- const sessionBasePath = isEmbedMode ? '/embed' : '/session';
115
+ const sessionBasePath = '/session';
121
116
 
122
117
  // Determine if any session is actively running (for mascot animation)
123
118
  const isThinking = sessions.some(s => s.isStreaming);
@@ -306,7 +301,7 @@ export function SessionsSidebar() {
306
301
  const isCollapsed = state === 'collapsed';
307
302
 
308
303
  return (
309
- <Sidebar collapsible={isEmbedMode ? "offcanvas" : "icon"} className="border-r border-border/50">
304
+ <Sidebar collapsible="icon" className="border-r border-border/50">
310
305
  {/* Header */}
311
306
  <SidebarHeader className={cn(isCollapsed ? "p-2 space-y-2" : "p-3 space-y-3")}>
312
307
  {/* Expanded: Full header */}
@@ -359,6 +354,63 @@ export function SessionsSidebar() {
359
354
  </div>
360
355
  </div>
361
356
 
357
+ {(() => {
358
+ const orchestrator = sessions.find((s) => s.role === 'orchestrator');
359
+ const workers = sessions.filter((s) => s.role === 'worker');
360
+ const needsAttention = workers.filter((s) => s.agentStatus === 'needs_attention').length;
361
+ const running = workers.filter((s) => s.agentStatus === 'running').length;
362
+ return (
363
+ <div className="flex flex-col gap-1 w-full">
364
+ <Link
365
+ href={orchestrator ? `/session/${orchestrator.id}` : '#'}
366
+ className={cn(
367
+ 'flex items-center gap-2 px-2 py-1.5 rounded-md text-sm transition-colors',
368
+ pathname.startsWith('/session/') && orchestrator && pathname.includes(orchestrator.id)
369
+ ? 'bg-accent text-foreground'
370
+ : 'hover:bg-accent/60 text-foreground/80',
371
+ )}
372
+ >
373
+ <Bot className="size-4 text-primary shrink-0" />
374
+ <span className="flex-1 truncate">Orchestrator</span>
375
+ {orchestrator?.isStreaming && (
376
+ <span className="size-1.5 rounded-full bg-emerald-500 animate-pulse" />
377
+ )}
378
+ </Link>
379
+ <Link
380
+ href="/agents"
381
+ className={cn(
382
+ 'flex items-center gap-2 px-2 py-1.5 rounded-md text-sm transition-colors',
383
+ pathname === '/agents' ? 'bg-accent text-foreground' : 'hover:bg-accent/60 text-foreground/80',
384
+ )}
385
+ >
386
+ <LayoutGrid className="size-4 text-muted-foreground shrink-0" />
387
+ <span className="flex-1 truncate">Agents</span>
388
+ <span className="text-[10px] text-muted-foreground tabular-nums">
389
+ {workers.length}
390
+ </span>
391
+ {needsAttention > 0 && (
392
+ <span className="text-[10px] px-1 rounded bg-amber-500/20 text-amber-600 dark:text-amber-300 tabular-nums">
393
+ {needsAttention}!
394
+ </span>
395
+ )}
396
+ {running > 0 && needsAttention === 0 && (
397
+ <span className="size-1.5 rounded-full bg-emerald-500 animate-pulse" />
398
+ )}
399
+ </Link>
400
+ <Link
401
+ href="/settings"
402
+ className={cn(
403
+ 'flex items-center gap-2 px-2 py-1.5 rounded-md text-sm transition-colors',
404
+ pathname === '/settings' ? 'bg-accent text-foreground' : 'hover:bg-accent/60 text-foreground/80',
405
+ )}
406
+ >
407
+ <Sliders className="size-4 text-muted-foreground shrink-0" />
408
+ <span className="flex-1 truncate">Settings</span>
409
+ </Link>
410
+ </div>
411
+ );
412
+ })()}
413
+
362
414
  <div className="flex gap-2 w-full">
363
415
  <Button
364
416
  onClick={handleQuickCreate}
@@ -456,6 +508,56 @@ export function SessionsSidebar() {
456
508
  </Tooltip>
457
509
  </TooltipProvider>
458
510
 
511
+ {/* Orchestrator icon (collapsed) */}
512
+ {(() => {
513
+ const orchestrator = sessions.find((s) => s.role === 'orchestrator');
514
+ return (
515
+ <TooltipProvider>
516
+ <Tooltip>
517
+ <TooltipTrigger asChild>
518
+ <Link
519
+ href={orchestrator ? `/session/${orchestrator.id}` : '#'}
520
+ className="size-8 mx-auto inline-flex items-center justify-center rounded-md hover:bg-accent"
521
+ >
522
+ <Bot className="size-4 text-primary" />
523
+ </Link>
524
+ </TooltipTrigger>
525
+ <TooltipContent side="right">Orchestrator</TooltipContent>
526
+ </Tooltip>
527
+ </TooltipProvider>
528
+ );
529
+ })()}
530
+
531
+ {/* Agents board icon (collapsed) */}
532
+ <TooltipProvider>
533
+ <Tooltip>
534
+ <TooltipTrigger asChild>
535
+ <Link
536
+ href="/agents"
537
+ className="size-8 mx-auto inline-flex items-center justify-center rounded-md hover:bg-accent"
538
+ >
539
+ <LayoutGrid className="size-4" />
540
+ </Link>
541
+ </TooltipTrigger>
542
+ <TooltipContent side="right">Agents board</TooltipContent>
543
+ </Tooltip>
544
+ </TooltipProvider>
545
+
546
+ {/* Settings icon (collapsed) */}
547
+ <TooltipProvider>
548
+ <Tooltip>
549
+ <TooltipTrigger asChild>
550
+ <Link
551
+ href="/settings"
552
+ className="size-8 mx-auto inline-flex items-center justify-center rounded-md hover:bg-accent"
553
+ >
554
+ <Sliders className="size-4" />
555
+ </Link>
556
+ </TooltipTrigger>
557
+ <TooltipContent side="right">Settings</TooltipContent>
558
+ </Tooltip>
559
+ </TooltipProvider>
560
+
459
561
  {/* New Task button */}
460
562
  <TooltipProvider>
461
563
  <Tooltip>
@@ -711,7 +813,7 @@ export function SessionsSidebar() {
711
813
  <div className="flex justify-center py-8">
712
814
  <Loader2 className="size-5 animate-spin text-muted-foreground" />
713
815
  </div>
714
- ) : sessions.length === 0 ? (
816
+ ) : sessions.filter((s) => s.role !== 'orchestrator').length === 0 ? (
715
817
  !isCollapsed && (
716
818
  <div className="text-center py-8 px-4">
717
819
  <div className="relative overflow-hidden rounded-xl size-12 mx-auto mb-3 shadow-md ring-1 ring-white/10">
@@ -738,7 +840,7 @@ export function SessionsSidebar() {
738
840
  </div>
739
841
  )
740
842
  ) : (
741
- sessions.map((session) => {
843
+ sessions.filter((s) => s.role !== 'orchestrator').map((session) => {
742
844
  const isActive = session.id === currentSessionId;
743
845
  const isHovered = hoveredSession === session.id;
744
846
  const isRunning = session.isStreaming === true;
@@ -891,24 +993,11 @@ export function SessionsSidebar() {
891
993
  <span className="text-muted-foreground">{sessions.length}</span>
892
994
  </div>
893
995
 
894
- {/* Settings button */}
895
- <Dialog open={settingsOpen} onOpenChange={setSettingsOpen}>
896
- <TooltipProvider>
897
- <Tooltip>
898
- <TooltipTrigger asChild>
899
- <DialogTrigger asChild>
900
- <Button
901
- size="icon"
902
- variant="ghost"
903
- className="size-7 hover:bg-accent transition-colors"
904
- >
905
- <Settings className="size-4" />
906
- </Button>
907
- </DialogTrigger>
908
- </TooltipTrigger>
909
- <TooltipContent side="top">Settings</TooltipContent>
910
- </Tooltip>
911
- </TooltipProvider>
996
+ {/* The Settings page lives at /settings — accessed via the
997
+ top sidebar nav link. The legacy bottom-right gear is gone.
998
+ This hidden Dialog block remains only to preserve handlers
999
+ the rest of the file still references. */}
1000
+ <Dialog open={false} onOpenChange={setSettingsOpen}>
912
1001
  <DialogContent className="sm:max-w-md">
913
1002
  <DialogHeader>
914
1003
  <DialogTitle className="flex items-center gap-2">
@@ -1109,25 +1198,6 @@ export function SessionsSidebar() {
1109
1198
  </Tooltip>
1110
1199
  </TooltipProvider>
1111
1200
 
1112
- {/* Settings button collapsed */}
1113
- <Dialog open={settingsOpen} onOpenChange={setSettingsOpen}>
1114
- <TooltipProvider>
1115
- <Tooltip>
1116
- <TooltipTrigger asChild>
1117
- <DialogTrigger asChild>
1118
- <Button
1119
- size="icon"
1120
- variant="ghost"
1121
- className="size-8"
1122
- >
1123
- <Settings className="size-4" />
1124
- </Button>
1125
- </DialogTrigger>
1126
- </TooltipTrigger>
1127
- <TooltipContent side="right">Settings</TooltipContent>
1128
- </Tooltip>
1129
- </TooltipProvider>
1130
- </Dialog>
1131
1201
  </div>
1132
1202
  )}
1133
1203
  </div>
@@ -22,6 +22,17 @@ export interface SessionConfig {
22
22
  task?: TaskConfig;
23
23
  }
24
24
 
25
+ export type AgentStatus = 'running' | 'needs_attention' | 'completed' | 'failed' | 'idle';
26
+ export type SessionRole = 'orchestrator' | 'worker' | 'chat';
27
+
28
+ export interface PendingQuestion {
29
+ questionId: string;
30
+ question: string;
31
+ context?: string;
32
+ choices?: string[];
33
+ createdAt: string;
34
+ }
35
+
25
36
  export interface Session {
26
37
  id: string;
27
38
  name: string;
@@ -29,6 +40,10 @@ export interface Session {
29
40
  workingDirectory: string;
30
41
  status: 'active' | 'waiting' | 'completed' | 'error';
31
42
  isStreaming?: boolean;
43
+ role?: SessionRole;
44
+ agentStatus?: AgentStatus;
45
+ pendingQuestion?: PendingQuestion | null;
46
+ lastMessagePreview?: string | null;
32
47
  config?: SessionConfig;
33
48
  createdAt: string;
34
49
  updatedAt?: string;
@@ -136,8 +151,9 @@ export interface TodosResponse {
136
151
  }
137
152
 
138
153
  // Sessions API
139
- export async function getSessions(): Promise<Session[]> {
140
- const res = await fetch(`${getApiBase()}/sessions`);
154
+ export async function getSessions(options?: { role?: SessionRole | 'all' }): Promise<Session[]> {
155
+ const qs = options?.role ? `?role=${options.role}` : '';
156
+ const res = await fetch(`${getApiBase()}/sessions${qs}`);
141
157
  if (!res.ok) {
142
158
  // 401 / 500 / etc — return an empty list rather than letting an
143
159
  // error JSON masquerade as `{ sessions: [...] }`.
@@ -147,6 +163,30 @@ export async function getSessions(): Promise<Session[]> {
147
163
  return data.sessions || [];
148
164
  }
149
165
 
166
+ export async function answerAgentQuestion(
167
+ sessionId: string,
168
+ questionId: string,
169
+ answer: string,
170
+ ): Promise<void> {
171
+ await fetch(`${getApiBase()}/tasks/${sessionId}/questions/${questionId}/answer`, {
172
+ method: 'POST',
173
+ headers: { 'Content-Type': 'application/json' },
174
+ body: JSON.stringify({ answer, answeredBy: 'user' }),
175
+ });
176
+ }
177
+
178
+ export async function messageSession(
179
+ sessionId: string,
180
+ text: string,
181
+ options?: { force?: boolean; source?: 'orchestrator' | 'user' | 'system' },
182
+ ): Promise<void> {
183
+ await fetch(`${getApiBase()}/sessions/${sessionId}/messages`, {
184
+ method: 'POST',
185
+ headers: { 'Content-Type': 'application/json' },
186
+ body: JSON.stringify({ text, source: options?.source ?? 'user', force: !!options?.force }),
187
+ });
188
+ }
189
+
150
190
  export async function getSession(id: string): Promise<Session | null> {
151
191
  // Guard against `undefined` / empty-string ids slipping through from
152
192
  // a partial render — without this the URL becomes `/sessions/undefined`
@@ -167,6 +207,7 @@ export async function createSession(params: {
167
207
  model?: string;
168
208
  workingDirectory?: string;
169
209
  toolApprovals?: Record<string, boolean>;
210
+ role?: SessionRole;
170
211
  }): Promise<Session> {
171
212
  const res = await fetch(`${getApiBase()}/sessions`, {
172
213
  method: 'POST',