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.
- package/dist/agent/index.d.ts +3 -3
- package/dist/agent/index.js +1258 -59
- package/dist/agent/index.js.map +1 -1
- package/dist/cli.js +10659 -8977
- package/dist/cli.js.map +1 -1
- package/dist/db/index.d.ts +2 -2
- package/dist/{index-BvIissiB.d.ts → index-D5l-DMGC.d.ts} +390 -6
- package/dist/index.d.ts +5 -5
- package/dist/index.js +9985 -8095
- package/dist/index.js.map +1 -1
- package/dist/{schema-CohdIL13.d.ts → schema-ecQSnCMz.d.ts} +41 -0
- package/dist/server/index.js +8852 -6970
- package/dist/server/index.js.map +1 -1
- package/dist/skills/default/manage-mcp.md +94 -0
- package/dist/skills/default/search-conversations.md +100 -0
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/index.js +111 -2
- package/dist/tools/index.js.map +1 -1
- package/package.json +5 -1
- package/src/skills/default/manage-mcp.md +94 -0
- package/src/skills/default/search-conversations.md +100 -0
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/app-path-routes-manifest.json +2 -1
- package/web/.next/standalone/web/.next/build-manifest.json +5 -5
- package/web/.next/standalone/web/.next/prerender-manifest.json +51 -3
- package/web/.next/standalone/web/.next/routes-manifest.json +13 -24
- package/web/.next/standalone/web/.next/server/app/(main)/agents/page/app-paths-manifest.json +3 -0
- package/web/.next/standalone/web/.next/server/app/{embed/[id] → (main)/agents}/page/build-manifest.json +3 -3
- package/web/.next/standalone/web/.next/server/app/{embed/[id] → (main)/agents}/page/next-font-manifest.json +1 -1
- package/web/.next/standalone/web/.next/server/app/{embed/[id] → (main)/agents}/page.js +7 -6
- package/web/.next/standalone/web/.next/server/app/(main)/agents/page.js.nft.json +1 -0
- package/web/.next/standalone/web/.next/server/app/(main)/agents/page_client-reference-manifest.js +2 -0
- package/web/.next/standalone/web/.next/server/app/(main)/page/build-manifest.json +3 -3
- package/web/.next/standalone/web/.next/server/app/(main)/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page/build-manifest.json +3 -3
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/settings/page/app-paths-manifest.json +3 -0
- package/web/.next/standalone/web/.next/server/app/(main)/settings/page/build-manifest.json +18 -0
- package/web/.next/standalone/web/.next/server/app/(main)/settings/page/next-font-manifest.json +11 -0
- package/web/.next/standalone/web/.next/server/app/(main)/settings/page/react-loadable-manifest.json +1 -0
- package/web/.next/standalone/web/.next/server/app/(main)/settings/page/server-reference-manifest.json +4 -0
- package/web/.next/standalone/web/.next/server/app/(main)/settings/page.js +21 -0
- package/web/.next/standalone/web/.next/server/app/(main)/settings/page.js.map +5 -0
- package/web/.next/standalone/web/.next/server/app/(main)/settings/page.js.nft.json +1 -0
- package/web/.next/standalone/web/.next/server/app/(main)/settings/page_client-reference-manifest.js +2 -0
- package/web/.next/standalone/web/.next/server/app/_global-error/page/build-manifest.json +3 -3
- package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found/page/build-manifest.json +3 -3
- package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/agents.html +1 -0
- package/web/.next/standalone/web/.next/server/app/agents.meta +16 -0
- package/web/.next/standalone/web/.next/server/app/agents.rsc +25 -0
- package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents/__PAGE__.segment.rsc +9 -0
- package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents.segment.rsc +4 -0
- package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p.segment.rsc +7 -0
- package/web/.next/standalone/web/.next/server/app/agents.segments/_full.segment.rsc +25 -0
- package/web/.next/standalone/web/.next/server/app/agents.segments/_head.segment.rsc +6 -0
- package/web/.next/standalone/web/.next/server/app/agents.segments/_index.segment.rsc +7 -0
- package/web/.next/standalone/web/.next/server/app/agents.segments/_tree.segment.rsc +5 -0
- package/web/.next/standalone/web/.next/server/app/api/config/route.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/api/health/route.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation/page/build-manifest.json +3 -3
- package/web/.next/standalone/web/.next/server/app/docs/installation/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/page/build-manifest.json +3 -3
- package/web/.next/standalone/web/.next/server/app/docs/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills/page/build-manifest.json +3 -3
- package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools/page/build-manifest.json +3 -3
- package/web/.next/standalone/web/.next/server/app/docs/tools/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.html +1 -1
- package/web/.next/standalone/web/.next/server/app/index.rsc +4 -4
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +4 -4
- package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/settings.html +1 -0
- package/web/.next/standalone/web/.next/server/app/settings.meta +16 -0
- package/web/.next/standalone/web/.next/server/app/settings.rsc +25 -0
- package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings/__PAGE__.segment.rsc +9 -0
- package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings.segment.rsc +4 -0
- package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p.segment.rsc +7 -0
- package/web/.next/standalone/web/.next/server/app/settings.segments/_full.segment.rsc +25 -0
- package/web/.next/standalone/web/.next/server/app/settings.segments/_head.segment.rsc +6 -0
- package/web/.next/standalone/web/.next/server/app/settings.segments/_index.segment.rsc +7 -0
- package/web/.next/standalone/web/.next/server/app/settings.segments/_tree.segment.rsc +5 -0
- package/web/.next/standalone/web/.next/server/app-paths-manifest.json +2 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_60d8842c._.js → 2374f_3b04c7b5._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_2801b766._.js → 2374f_5ee1ee50._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_c13c8f4f._.js → 2374f_7b7dd4c7._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_b7f45fdf._.js → 2374f_9e444fb0._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_a383a4d9._.js → 2374f_b57914d2._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_c2c47039._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_806bd012._.js → 2374f_db1d6704._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_f363c084._.js → 2374f_e6dbbf5d._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_lucide-react_dist_esm_icons_50c2f239._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__2ea52390._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__6097da17._.js +15 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__9f149e88._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__b050bb8f._.js +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__c94db61a._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_4fe3c244._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/{web_3c2b112b._.js → web_7ca56356._.js} +3 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_8e76ee8b._.js +8 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_90d4125e._.js +7 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_(main)_agents_page_actions_30f6e448.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_(main)_settings_page_actions_7285839d.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_b38a47ee._.js +4 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_f7cf6b63._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_app_(main)_layout_tsx_453f6492._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_app_(main)_page_tsx_5ac4794b._.js +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_app_(main)_settings_page_tsx_eb320e07._.js +33 -0
- package/web/.next/standalone/web/.next/server/middleware-build-manifest.js +3 -3
- package/web/.next/standalone/web/.next/server/next-font-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/next-font-manifest.json +8 -4
- package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
- package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
- package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
- package/web/.next/standalone/web/.next/static/chunks/03b5edce6d5b809e.js +1 -0
- package/web/.next/standalone/web/.next/static/chunks/29dcecc3c2ca92b0.js +1 -0
- package/web/.next/standalone/web/.next/static/chunks/344be859c2c8600b.css +1 -0
- package/web/.next/standalone/web/.next/static/chunks/545725e4c1237026.js +7 -0
- package/web/.next/standalone/web/.next/static/chunks/60359bdd369c0c72.js +1 -0
- package/web/.next/standalone/web/.next/static/chunks/699803c4fb2dd3fc.js +5 -0
- package/web/.next/standalone/web/.next/static/chunks/735a2408c315b2f0.js +1 -0
- package/web/.next/standalone/web/.next/static/chunks/d54077a2bb8314ed.js +31 -0
- package/web/.next/standalone/web/.next/static/chunks/dc34aa94e57fa28e.js +3 -0
- package/web/.next/standalone/web/.next/static/chunks/ea89ca7892d8c557.js +1 -0
- package/web/.next/standalone/web/.next/static/chunks/f0f19357f3fb7cf8.js +1 -0
- package/web/.next/standalone/web/.next/static/chunks/f50a66c24c564585.js +13 -0
- package/web/.next/standalone/web/.next/static/chunks/f5fe518b79d1bf41.js +1 -0
- package/web/.next/{static/chunks/turbopack-597558bb7b6982f6.js → standalone/web/.next/static/chunks/turbopack-2c0905c7bbebae3f.js} +1 -1
- package/web/.next/standalone/web/.next/static/static/chunks/03b5edce6d5b809e.js +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/29dcecc3c2ca92b0.js +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/344be859c2c8600b.css +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/545725e4c1237026.js +7 -0
- package/web/.next/standalone/web/.next/static/static/chunks/60359bdd369c0c72.js +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/699803c4fb2dd3fc.js +5 -0
- package/web/.next/standalone/web/.next/static/static/chunks/735a2408c315b2f0.js +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/d54077a2bb8314ed.js +31 -0
- package/web/.next/standalone/web/.next/static/static/chunks/dc34aa94e57fa28e.js +3 -0
- package/web/.next/standalone/web/.next/static/static/chunks/ea89ca7892d8c557.js +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/f0f19357f3fb7cf8.js +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/f50a66c24c564585.js +13 -0
- package/web/.next/standalone/web/.next/static/static/chunks/f5fe518b79d1bf41.js +1 -0
- package/web/.next/standalone/web/.next/static/{chunks/turbopack-597558bb7b6982f6.js → static/chunks/turbopack-2c0905c7bbebae3f.js} +1 -1
- package/web/.next/standalone/web/next.config.ts +1 -55
- package/web/.next/standalone/web/package-lock.json +3 -3
- package/web/.next/standalone/web/src/app/(main)/agents/page.tsx +222 -0
- package/web/.next/standalone/web/src/app/(main)/page.tsx +40 -1
- package/web/.next/standalone/web/src/app/(main)/session/[id]/page.tsx +9 -1
- package/web/.next/standalone/web/src/app/(main)/settings/page.tsx +1116 -0
- package/web/.next/standalone/web/src/components/chat-interface.tsx +205 -4
- package/web/.next/standalone/web/src/components/pending-question-banner.tsx +106 -0
- package/web/.next/standalone/web/src/components/sessions-sidebar.tsx +120 -50
- package/web/.next/standalone/web/src/lib/api.ts +43 -2
- package/web/.next/static/chunks/03b5edce6d5b809e.js +1 -0
- package/web/.next/static/chunks/29dcecc3c2ca92b0.js +1 -0
- package/web/.next/static/chunks/344be859c2c8600b.css +1 -0
- package/web/.next/static/chunks/545725e4c1237026.js +7 -0
- package/web/.next/static/chunks/60359bdd369c0c72.js +1 -0
- package/web/.next/static/chunks/699803c4fb2dd3fc.js +5 -0
- package/web/.next/static/chunks/735a2408c315b2f0.js +1 -0
- package/web/.next/static/chunks/d54077a2bb8314ed.js +31 -0
- package/web/.next/static/chunks/dc34aa94e57fa28e.js +3 -0
- package/web/.next/static/chunks/ea89ca7892d8c557.js +1 -0
- package/web/.next/static/chunks/f0f19357f3fb7cf8.js +1 -0
- package/web/.next/static/chunks/f50a66c24c564585.js +13 -0
- package/web/.next/static/chunks/f5fe518b79d1bf41.js +1 -0
- package/web/.next/{standalone/web/.next/static/static/chunks/turbopack-597558bb7b6982f6.js → static/chunks/turbopack-2c0905c7bbebae3f.js} +1 -1
- package/web/.next/standalone/web/.next/server/app/embed/[id]/page/app-paths-manifest.json +0 -3
- package/web/.next/standalone/web/.next/server/app/embed/[id]/page.js.nft.json +0 -1
- package/web/.next/standalone/web/.next/server/app/embed/[id]/page_client-reference-manifest.js +0 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_317b1fef._.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_37dd9702._.js +0 -45
- package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_4d44e4ed._.js +0 -26
- package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_54ac917f._.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_86585101._.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_c59a35bb._.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_fdfc7f3d._.js +0 -31
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__234f92d8._.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__9a826344._.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__9d3a7cbf._.js +0 -15
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__de76483d._.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_08242997._.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_123ffe97._.js +0 -8
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_5cca707f._.js +0 -7
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_935e81f5._.js +0 -7
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_99b01335._.js +0 -8
- package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_embed_[id]_page_actions_dd0b7fea.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_components_sessions-sidebar_tsx_92510070._.js +0 -3
- package/web/.next/standalone/web/.next/static/chunks/1ebba7ac024244f9.js +0 -5
- package/web/.next/standalone/web/.next/static/chunks/275e8268daf318b2.js +0 -7
- package/web/.next/standalone/web/.next/static/chunks/2bf377e04592f3c8.js +0 -13
- package/web/.next/standalone/web/.next/static/chunks/376a123113ccf5eb.js +0 -3
- package/web/.next/standalone/web/.next/static/chunks/58fd0aaa2746b444.js +0 -1
- package/web/.next/standalone/web/.next/static/chunks/61c9922b38a9569d.js +0 -3
- package/web/.next/standalone/web/.next/static/chunks/74b64476a24dd71e.css +0 -1
- package/web/.next/standalone/web/.next/static/chunks/767bcdfbabf0703e.js +0 -7
- package/web/.next/standalone/web/.next/static/chunks/a888d448ceab1abe.js +0 -1
- package/web/.next/standalone/web/.next/static/chunks/b9ad1584d4e11d12.js +0 -1
- package/web/.next/standalone/web/.next/static/chunks/c6f40df16a9396b9.js +0 -1
- package/web/.next/standalone/web/.next/static/chunks/ea29be392100ab0f.js +0 -5
- package/web/.next/standalone/web/.next/static/static/chunks/1ebba7ac024244f9.js +0 -5
- package/web/.next/standalone/web/.next/static/static/chunks/275e8268daf318b2.js +0 -7
- package/web/.next/standalone/web/.next/static/static/chunks/2bf377e04592f3c8.js +0 -13
- package/web/.next/standalone/web/.next/static/static/chunks/376a123113ccf5eb.js +0 -3
- package/web/.next/standalone/web/.next/static/static/chunks/58fd0aaa2746b444.js +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/61c9922b38a9569d.js +0 -3
- package/web/.next/standalone/web/.next/static/static/chunks/74b64476a24dd71e.css +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/767bcdfbabf0703e.js +0 -7
- package/web/.next/standalone/web/.next/static/static/chunks/a888d448ceab1abe.js +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/b9ad1584d4e11d12.js +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/c6f40df16a9396b9.js +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/ea29be392100ab0f.js +0 -5
- package/web/.next/standalone/web/src/app/embed/[id]/page.tsx +0 -77
- package/web/.next/standalone/web/src/lib/embed-bootstrap.ts +0 -108
- package/web/.next/static/chunks/1ebba7ac024244f9.js +0 -5
- package/web/.next/static/chunks/275e8268daf318b2.js +0 -7
- package/web/.next/static/chunks/2bf377e04592f3c8.js +0 -13
- package/web/.next/static/chunks/376a123113ccf5eb.js +0 -3
- package/web/.next/static/chunks/58fd0aaa2746b444.js +0 -1
- package/web/.next/static/chunks/61c9922b38a9569d.js +0 -3
- package/web/.next/static/chunks/74b64476a24dd71e.css +0 -1
- package/web/.next/static/chunks/767bcdfbabf0703e.js +0 -7
- package/web/.next/static/chunks/a888d448ceab1abe.js +0 -1
- package/web/.next/static/chunks/b9ad1584d4e11d12.js +0 -1
- package/web/.next/static/chunks/c6f40df16a9396b9.js +0 -1
- package/web/.next/static/chunks/ea29be392100ab0f.js +0 -5
- package/dist/{search-CCffrVJE.d.ts → search-DOzC4ojH.d.ts} +1 -1
- /package/web/.next/standalone/web/.next/server/app/{embed/[id] → (main)/agents}/page/react-loadable-manifest.json +0 -0
- /package/web/.next/standalone/web/.next/server/app/{embed/[id] → (main)/agents}/page/server-reference-manifest.json +0 -0
- /package/web/.next/standalone/web/.next/server/app/{embed/[id] → (main)/agents}/page.js.map +0 -0
- /package/web/.next/standalone/web/.next/static/{WCqUmRTRCgZqwBVGKQESX → static/yyzMTo2RYusiXE0e1_kdc}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{WCqUmRTRCgZqwBVGKQESX → static/yyzMTo2RYusiXE0e1_kdc}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{WCqUmRTRCgZqwBVGKQESX → static/yyzMTo2RYusiXE0e1_kdc}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{static/WCqUmRTRCgZqwBVGKQESX → yyzMTo2RYusiXE0e1_kdc}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{static/WCqUmRTRCgZqwBVGKQESX → yyzMTo2RYusiXE0e1_kdc}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{static/WCqUmRTRCgZqwBVGKQESX → yyzMTo2RYusiXE0e1_kdc}/_ssgManifest.js +0 -0
- /package/web/.next/static/{WCqUmRTRCgZqwBVGKQESX → yyzMTo2RYusiXE0e1_kdc}/_buildManifest.js +0 -0
- /package/web/.next/static/{WCqUmRTRCgZqwBVGKQESX → yyzMTo2RYusiXE0e1_kdc}/_clientMiddlewareManifest.json +0 -0
- /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
|
-
<
|
|
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
|
-
|
|
3036
|
-
|
|
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 =
|
|
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=
|
|
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
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
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
|
|
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',
|