sparkecoder 0.1.98 → 0.1.100
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/README.md +41 -41
- package/dist/agent/index.d.ts +3 -3
- package/dist/agent/index.js +1274 -59
- package/dist/agent/index.js.map +1 -1
- package/dist/cli.js +10671 -8973
- 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 +10002 -8096
- package/dist/index.js.map +1 -1
- package/dist/{schema-CohdIL13.d.ts → schema-ecQSnCMz.d.ts} +41 -0
- package/dist/server/index.js +8867 -6969
- 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 +3 -3
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- 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 +3 -3
- 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 +4 -4
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +4 -4
- 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 +3 -3
- 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 +2 -2
- 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 +2 -2
- 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 +4 -4
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +4 -4
- 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 +3 -3
- 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 +2 -2
- 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 +4 -4
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +4 -4
- 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 +3 -3
- 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 +2 -2
- 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 +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.rsc +4 -4
- package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +4 -4
- 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 +3 -3
- 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 +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.html +1 -1
- package/web/.next/standalone/web/.next/server/app/index.rsc +5 -5
- 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 +5 -5
- 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 +3 -3
- 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_a383a4d9._.js → 2374f_1f3f2d00._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_60d8842c._.js → 2374f_38945fd9._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_f363c084._.js → 2374f_570c34dc._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_806bd012._.js → 2374f_9c560f3a._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_b7f45fdf._.js → 2374f_a0d5caeb._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_c13c8f4f._.js → 2374f_c87abaf4._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_2801b766._.js → 2374f_d8122230._.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]__be5e2967._.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_3b9a2423._.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/4239395558fab3ef.js +1 -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/ae4bb24474ff1ed0.js +13 -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/f5fe518b79d1bf41.js +1 -0
- package/web/.next/standalone/web/.next/static/chunks/fbdcbd65f9a38f4b.js +7 -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/4239395558fab3ef.js +1 -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/ae4bb24474ff1ed0.js +13 -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/f5fe518b79d1bf41.js +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/fbdcbd65f9a38f4b.js +7 -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 +7 -7
- 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/ai-elements/mention-input.tsx +125 -17
- 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/4239395558fab3ef.js +1 -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/ae4bb24474ff1ed0.js +13 -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/f5fe518b79d1bf41.js +1 -0
- package/web/.next/static/chunks/fbdcbd65f9a38f4b.js +7 -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/9fce2ce79c4c834e.js +0 -1
- 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/9fce2ce79c4c834e.js +0 -1
- 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/9fce2ce79c4c834e.js +0 -1
- 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 → eyxBRyMA8Zq36Pfen3Ylj}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{WCqUmRTRCgZqwBVGKQESX → eyxBRyMA8Zq36Pfen3Ylj}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{WCqUmRTRCgZqwBVGKQESX → eyxBRyMA8Zq36Pfen3Ylj}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/static/{WCqUmRTRCgZqwBVGKQESX → eyxBRyMA8Zq36Pfen3Ylj}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/static/{WCqUmRTRCgZqwBVGKQESX → eyxBRyMA8Zq36Pfen3Ylj}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/static/{WCqUmRTRCgZqwBVGKQESX → eyxBRyMA8Zq36Pfen3Ylj}/_ssgManifest.js +0 -0
- /package/web/.next/static/{WCqUmRTRCgZqwBVGKQESX → eyxBRyMA8Zq36Pfen3Ylj}/_buildManifest.js +0 -0
- /package/web/.next/static/{WCqUmRTRCgZqwBVGKQESX → eyxBRyMA8Zq36Pfen3Ylj}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{WCqUmRTRCgZqwBVGKQESX → eyxBRyMA8Zq36Pfen3Ylj}/_ssgManifest.js +0 -0
|
@@ -59,8 +59,15 @@ export interface SlashCommand {
|
|
|
59
59
|
action?: () => void;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
interface McpServer {
|
|
63
|
+
id: string;
|
|
64
|
+
name: string;
|
|
65
|
+
enabled: boolean;
|
|
66
|
+
transport: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
62
69
|
interface TriggerState {
|
|
63
|
-
type: "file" | "command";
|
|
70
|
+
type: "file" | "command" | "mcp";
|
|
64
71
|
query: string;
|
|
65
72
|
startIndex: number; // Where the trigger character is
|
|
66
73
|
endIndex: number; // Current cursor position
|
|
@@ -87,8 +94,9 @@ export function useMentionInput() {
|
|
|
87
94
|
return ctx;
|
|
88
95
|
}
|
|
89
96
|
|
|
90
|
-
// Regex to match @mentions in text (
|
|
91
|
-
|
|
97
|
+
// Regex to match @mentions in text. Captures both file-style (`@path/to/file`,
|
|
98
|
+
// `@folder/`) and MCP-style (`@mcp/<server-name>`).
|
|
99
|
+
const MENTION_REGEX = /@(mcp\/[\w-]+|[\w./-]+\/?)/g;
|
|
92
100
|
|
|
93
101
|
// ============================================================================
|
|
94
102
|
// Default Slash Commands - Each inserts a prompt template
|
|
@@ -311,6 +319,36 @@ export function MentionTextarea({
|
|
|
311
319
|
limit: 15,
|
|
312
320
|
});
|
|
313
321
|
|
|
322
|
+
// Fetch enabled MCP servers when either the dedicated @mcp trigger is
|
|
323
|
+
// active OR the user is typing a plain @<query> that might match a server
|
|
324
|
+
// name (e.g. `@gmail`, `@github`). Server names appear alongside files
|
|
325
|
+
// in the popover.
|
|
326
|
+
const [mcpServers, setMcpServers] = useState<McpServer[]>([]);
|
|
327
|
+
useEffect(() => {
|
|
328
|
+
if (trigger?.type !== "mcp" && trigger?.type !== "file") {
|
|
329
|
+
setMcpServers([]);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
let cancelled = false;
|
|
333
|
+
(async () => {
|
|
334
|
+
try {
|
|
335
|
+
const { getApiUrl } = await import("@/lib/config");
|
|
336
|
+
const res = await fetch(`${getApiUrl()}/api/mcp`);
|
|
337
|
+
if (!res.ok) return;
|
|
338
|
+
const data = await res.json();
|
|
339
|
+
if (cancelled) return;
|
|
340
|
+
const q = (trigger.query || "").toLowerCase();
|
|
341
|
+
const filtered: McpServer[] = (data.servers || [])
|
|
342
|
+
.filter((s: any) => s.enabled)
|
|
343
|
+
.filter((s: any) => !q || s.name.toLowerCase().includes(q));
|
|
344
|
+
setMcpServers(filtered);
|
|
345
|
+
} catch {
|
|
346
|
+
if (!cancelled) setMcpServers([]);
|
|
347
|
+
}
|
|
348
|
+
})();
|
|
349
|
+
return () => { cancelled = true; };
|
|
350
|
+
}, [trigger]);
|
|
351
|
+
|
|
314
352
|
// Filter slash commands based on query
|
|
315
353
|
const filteredCommands = useMemo(() => {
|
|
316
354
|
if (trigger?.type !== "command") return [];
|
|
@@ -322,8 +360,14 @@ export function MentionTextarea({
|
|
|
322
360
|
);
|
|
323
361
|
}, [trigger, slashCommands]);
|
|
324
362
|
|
|
325
|
-
// Current items to display
|
|
326
|
-
|
|
363
|
+
// Current items to display. For file mode we prepend any matching MCP
|
|
364
|
+
// servers so the same @-popover surfaces both. MCP names render in their
|
|
365
|
+
// own group above files (see CommandList below); we keep them sequenced
|
|
366
|
+
// first here so arrow-key navigation matches the visual order.
|
|
367
|
+
const items: Array<(typeof files)[0] | SlashCommand | McpServer> =
|
|
368
|
+
trigger?.type === "file" ? [...mcpServers, ...files]
|
|
369
|
+
: trigger?.type === "mcp" ? mcpServers
|
|
370
|
+
: filteredCommands;
|
|
327
371
|
const showPopover = trigger !== null && (items.length > 0 || isLoading);
|
|
328
372
|
|
|
329
373
|
// Calculate popover position based on textarea caret
|
|
@@ -408,6 +452,20 @@ export function MentionTextarea({
|
|
|
408
452
|
|
|
409
453
|
const beforeCursor = text.slice(0, cursorPos);
|
|
410
454
|
|
|
455
|
+
// Check for @mcp trigger (MCP server mention). Supports `@mcp/<query>`,
|
|
456
|
+
// `@mcp:<query>`, and bare `@mcp` (no separator yet).
|
|
457
|
+
const mcpMatch = beforeCursor.match(/@mcp(?:[\/:]([^\s@]*))?$/i);
|
|
458
|
+
if (mcpMatch) {
|
|
459
|
+
setTrigger({
|
|
460
|
+
type: "mcp",
|
|
461
|
+
query: mcpMatch[1] ?? "",
|
|
462
|
+
startIndex: cursorPos - mcpMatch[0].length,
|
|
463
|
+
endIndex: cursorPos,
|
|
464
|
+
});
|
|
465
|
+
setSelectedIndex(0);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
|
|
411
469
|
// Check for @ trigger (file mention)
|
|
412
470
|
const atMatch = beforeCursor.match(/@([^\s@]*)$/);
|
|
413
471
|
if (atMatch) {
|
|
@@ -453,9 +511,31 @@ export function MentionTextarea({
|
|
|
453
511
|
|
|
454
512
|
// Handle selection from popover
|
|
455
513
|
const handleSelect = useCallback(
|
|
456
|
-
(item: (typeof files)[0] | SlashCommand) => {
|
|
514
|
+
(item: (typeof files)[0] | SlashCommand | McpServer) => {
|
|
457
515
|
if (!trigger || !textareaRef.current) return;
|
|
458
516
|
|
|
517
|
+
// MCP server selection. Works under either the dedicated `@mcp/` trigger
|
|
518
|
+
// OR a plain `@<name>` file-trigger when the query happens to match a
|
|
519
|
+
// configured server (so `@gmail` works the same as `@mcp/gmail`).
|
|
520
|
+
if ("name" in item && "transport" in item) {
|
|
521
|
+
const mcp = item as McpServer;
|
|
522
|
+
const before = value.slice(0, trigger.startIndex);
|
|
523
|
+
const after = value.slice(trigger.endIndex);
|
|
524
|
+
const mentionText = `@mcp/${mcp.name} `;
|
|
525
|
+
const newValue = before + mentionText + after;
|
|
526
|
+
onChange(newValue);
|
|
527
|
+
setTimeout(() => {
|
|
528
|
+
if (textareaRef.current) {
|
|
529
|
+
const newPos = trigger.startIndex + mentionText.length;
|
|
530
|
+
textareaRef.current.selectionStart = newPos;
|
|
531
|
+
textareaRef.current.selectionEnd = newPos;
|
|
532
|
+
textareaRef.current.focus();
|
|
533
|
+
}
|
|
534
|
+
}, 0);
|
|
535
|
+
setTrigger(null);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
|
|
459
539
|
if (trigger.type === "file" && "path" in item) {
|
|
460
540
|
// Insert @path inline (replacing @query with @fullpath)
|
|
461
541
|
const before = value.slice(0, trigger.startIndex);
|
|
@@ -711,30 +791,58 @@ export function MentionTextarea({
|
|
|
711
791
|
<CommandEmpty>
|
|
712
792
|
{trigger?.type === "file"
|
|
713
793
|
? "No files found"
|
|
794
|
+
: trigger?.type === "mcp"
|
|
795
|
+
? "No MCP servers configured. Add one in Settings → MCP."
|
|
714
796
|
: "No commands found"}
|
|
715
797
|
</CommandEmpty>
|
|
716
798
|
)}
|
|
717
|
-
{trigger?.type === "file" &&
|
|
718
|
-
<CommandGroup heading="
|
|
719
|
-
{
|
|
799
|
+
{(trigger?.type === "mcp" || trigger?.type === "file") && mcpServers.length > 0 && (
|
|
800
|
+
<CommandGroup heading="MCP servers">
|
|
801
|
+
{mcpServers.map((s, index) => (
|
|
720
802
|
<CommandItem
|
|
721
|
-
key={
|
|
722
|
-
onSelect={() => handleSelect(
|
|
803
|
+
key={s.id}
|
|
804
|
+
onSelect={() => handleSelect(s)}
|
|
723
805
|
className={cn(
|
|
724
806
|
"cursor-pointer",
|
|
725
807
|
index === selectedIndex && "bg-accent"
|
|
726
808
|
)}
|
|
727
809
|
>
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
<
|
|
732
|
-
|
|
733
|
-
<span className="truncate">{file.path}</span>
|
|
810
|
+
<HashIcon className="mr-2 size-4 text-emerald-500" />
|
|
811
|
+
<div className="flex flex-col">
|
|
812
|
+
<span className="font-medium">{s.name}</span>
|
|
813
|
+
<span className="text-xs text-muted-foreground">{s.transport} · tools: mcp_{s.name}_*</span>
|
|
814
|
+
</div>
|
|
734
815
|
</CommandItem>
|
|
735
816
|
))}
|
|
736
817
|
</CommandGroup>
|
|
737
818
|
)}
|
|
819
|
+
{trigger?.type === "file" && files.length > 0 && (
|
|
820
|
+
<CommandGroup heading="Files & Folders">
|
|
821
|
+
{files.map((file, fileIdx) => {
|
|
822
|
+
// In file mode the items array is [mcpServers, ...files],
|
|
823
|
+
// so the per-file selection index is offset by the number
|
|
824
|
+
// of MCP rows rendered above.
|
|
825
|
+
const idx = mcpServers.length + fileIdx;
|
|
826
|
+
return (
|
|
827
|
+
<CommandItem
|
|
828
|
+
key={file.path}
|
|
829
|
+
onSelect={() => handleSelect(file)}
|
|
830
|
+
className={cn(
|
|
831
|
+
"cursor-pointer",
|
|
832
|
+
idx === selectedIndex && "bg-accent"
|
|
833
|
+
)}
|
|
834
|
+
>
|
|
835
|
+
{file.type === "folder" ? (
|
|
836
|
+
<FolderIcon className="mr-2 size-4 text-blue-500" />
|
|
837
|
+
) : (
|
|
838
|
+
<FileIcon className="mr-2 size-4 text-muted-foreground" />
|
|
839
|
+
)}
|
|
840
|
+
<span className="truncate">{file.path}</span>
|
|
841
|
+
</CommandItem>
|
|
842
|
+
);
|
|
843
|
+
})}
|
|
844
|
+
</CommandGroup>
|
|
845
|
+
)}
|
|
738
846
|
{trigger?.type === "command" && filteredCommands.length > 0 && (
|
|
739
847
|
<CommandGroup heading="Commands">
|
|
740
848
|
{filteredCommands.map((cmd, index) => (
|
|
@@ -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
|
+
}
|