@invergent/agent-chat-react 1.5.4 → 1.5.6

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/index.js CHANGED
@@ -14,9 +14,9 @@ import { useControllableState } from '@radix-ui/react-use-controllable-state';
14
14
  import { createHighlighter } from 'shiki';
15
15
  import { diffLines, diffWordsWithSpace } from 'diff';
16
16
  import Ansi from 'ansi-to-react';
17
- import { getUsage } from 'tokenlens';
18
17
  import { Command as Command$1 } from 'cmdk';
19
18
  import { nanoid } from 'nanoid';
19
+ import { getUsage } from 'tokenlens';
20
20
  import 'pdfjs-dist/web/pdf_viewer.css';
21
21
  import { formatDistanceToNow } from 'date-fns';
22
22
 
@@ -3307,7 +3307,7 @@ function ClarifyLocked({
3307
3307
  function ArtifactToolBlock({ tc }) {
3308
3308
  const args = parseArgs(tc.args) ?? {};
3309
3309
  const status = effectiveStatus(tc);
3310
- const label = status === "running" ? "Creating artifact\u2026" : status === "error" ? "Tried to create" : "Created";
3310
+ const label = status === "running" ? "Creating artifact\u2026" : status === "error" ? "Creating artifact\u2026" : "Created";
3311
3311
  return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-sm ", children: [
3312
3312
  /* @__PURE__ */ jsx("span", { className: "font-semibold text-foreground", children: label }),
3313
3313
  args.name && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground truncate", children: args.name })
@@ -3692,7 +3692,18 @@ var ChatMessage = memo(function ChatMessage2({
3692
3692
  onFileSelect
3693
3693
  }) {
3694
3694
  if (message.role === "user") {
3695
- return /* @__PURE__ */ jsx(Message, { from: "user", children: /* @__PURE__ */ jsx(MessageContent, { children: message.content }) });
3695
+ return /* @__PURE__ */ jsx(Message, { from: "user", children: /* @__PURE__ */ jsxs(MessageContent, { children: [
3696
+ message.content,
3697
+ message.images && message.images.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2 mt-2", children: message.images.map((img, i) => /* @__PURE__ */ jsx(
3698
+ "img",
3699
+ {
3700
+ src: img.data,
3701
+ alt: "Attached",
3702
+ className: "max-w-64 max-h-48 rounded-lg border border-border object-contain"
3703
+ },
3704
+ i
3705
+ )) })
3706
+ ] }) });
3696
3707
  }
3697
3708
  return /* @__PURE__ */ jsx(AssistantMessage, { message, isLast, onFileSelect });
3698
3709
  });
@@ -3718,491 +3729,172 @@ function AssistantMessage({
3718
3729
  ] })
3719
3730
  ] }) });
3720
3731
  }
3721
- function HoverCard({
3732
+ function InputGroup({ className, ...props }) {
3733
+ return /* @__PURE__ */ jsx(
3734
+ "div",
3735
+ {
3736
+ "data-slot": "input-group",
3737
+ role: "group",
3738
+ className: cn(
3739
+ "group/input-group relative flex h-10 w-full min-w-0 items-center rounded-none border border-transparent border-b-input bg-transparent transition-[color,border-color] outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-data-[align=block-end]:rounded-none has-data-[align=block-start]:rounded-none has-[[data-slot=input-group-control]:focus-visible]:border-b-ring has-[[data-slot][aria-invalid=true]]:border-b-destructive has-[textarea]:rounded-none has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:has-[[data-slot][aria-invalid=true]]:border-b-destructive/50 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3",
3740
+ className
3741
+ ),
3742
+ ...props
3743
+ }
3744
+ );
3745
+ }
3746
+ var inputGroupAddonVariants = cva(
3747
+ "flex h-auto cursor-text items-center justify-center gap-2 py-2 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 **:data-[slot=kbd]:rounded-none **:data-[slot=kbd]:bg-muted-foreground/10 **:data-[slot=kbd]:px-1.5 [&>svg:not([class*='size-'])]:size-3.5",
3748
+ {
3749
+ variants: {
3750
+ align: {
3751
+ "inline-start": "order-first",
3752
+ "inline-end": "order-last",
3753
+ "block-start": "order-first w-full justify-start pt-3 group-has-[>input]/input-group:pt-3.5 [.border-b]:pb-3.5",
3754
+ "block-end": "order-last w-full justify-start pb-3 group-has-[>input]/input-group:pb-3.5 [.border-t]:pt-3.5"
3755
+ }
3756
+ },
3757
+ defaultVariants: {
3758
+ align: "inline-start"
3759
+ }
3760
+ }
3761
+ );
3762
+ function InputGroupAddon({
3763
+ className,
3764
+ align = "inline-start",
3722
3765
  ...props
3723
3766
  }) {
3724
- return /* @__PURE__ */ jsx(HoverCard$1.Root, { "data-slot": "hover-card", ...props });
3767
+ return /* @__PURE__ */ jsx(
3768
+ "div",
3769
+ {
3770
+ role: "group",
3771
+ "data-slot": "input-group-addon",
3772
+ "data-align": align,
3773
+ className: cn(inputGroupAddonVariants({ align }), className),
3774
+ onClick: (e) => {
3775
+ if (e.target.closest("button")) {
3776
+ return;
3777
+ }
3778
+ e.currentTarget.parentElement?.querySelector("input")?.focus();
3779
+ },
3780
+ ...props
3781
+ }
3782
+ );
3725
3783
  }
3726
- function HoverCardTrigger({
3784
+ var inputGroupButtonVariants = cva(
3785
+ "flex items-center gap-2 rounded-none text-sm shadow-none",
3786
+ {
3787
+ variants: {
3788
+ size: {
3789
+ xs: "h-6 gap-1 rounded-none px-1.5 text-xs [&>svg:not([class*='size-'])]:size-3.5",
3790
+ sm: "",
3791
+ "icon-xs": "size-6 p-0 text-xs has-[>svg]:p-0",
3792
+ "icon-sm": "size-8 p-0 has-[>svg]:p-0"
3793
+ }
3794
+ },
3795
+ defaultVariants: {
3796
+ size: "xs"
3797
+ }
3798
+ }
3799
+ );
3800
+ function InputGroupButton({
3801
+ className,
3802
+ type = "button",
3803
+ variant = "ghost",
3804
+ size = "xs",
3727
3805
  ...props
3728
3806
  }) {
3729
- return /* @__PURE__ */ jsx(HoverCard$1.Trigger, { "data-slot": "hover-card-trigger", ...props });
3807
+ return /* @__PURE__ */ jsx(
3808
+ Button,
3809
+ {
3810
+ type,
3811
+ "data-size": size,
3812
+ variant,
3813
+ className: cn(inputGroupButtonVariants({ size }), className),
3814
+ ...props
3815
+ }
3816
+ );
3730
3817
  }
3731
- function HoverCardContent({
3818
+ function InputGroupTextarea({
3732
3819
  className,
3733
- align = "center",
3734
- sideOffset = 4,
3735
3820
  ...props
3736
3821
  }) {
3737
- return /* @__PURE__ */ jsx(HoverCard$1.Portal, { "data-slot": "hover-card-portal", children: /* @__PURE__ */ jsx(
3738
- HoverCard$1.Content,
3822
+ return /* @__PURE__ */ jsx(
3823
+ Textarea,
3739
3824
  {
3740
- "data-slot": "hover-card-content",
3741
- align,
3742
- sideOffset,
3825
+ "data-slot": "input-group-control",
3743
3826
  className: cn(
3744
- "z-50 w-72 origin-(--radix-hover-card-content-transform-origin) bg-popover p-4 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
3827
+ "flex-1 resize-none border-0 bg-transparent py-2.5 ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent",
3745
3828
  className
3746
3829
  ),
3747
3830
  ...props
3748
3831
  }
3749
- ) });
3832
+ );
3750
3833
  }
3751
- function Progress({
3834
+ function Command({
3752
3835
  className,
3753
- value,
3754
3836
  ...props
3755
3837
  }) {
3756
3838
  return /* @__PURE__ */ jsx(
3757
- Progress$1.Root,
3839
+ Command$1,
3758
3840
  {
3759
- "data-slot": "progress",
3841
+ "data-slot": "command",
3760
3842
  className: cn(
3761
- "relative flex h-0.5 w-full items-center overflow-x-hidden rounded-none bg-muted",
3843
+ "flex size-full flex-col overflow-hidden bg-popover text-popover-foreground",
3762
3844
  className
3763
3845
  ),
3764
- ...props,
3765
- children: /* @__PURE__ */ jsx(
3766
- Progress$1.Indicator,
3767
- {
3768
- "data-slot": "progress-indicator",
3769
- className: "size-full flex-1 bg-primary transition-all",
3770
- style: { transform: `translateX(-${100 - (value || 0)}%)` }
3771
- }
3772
- )
3846
+ ...props
3773
3847
  }
3774
3848
  );
3775
3849
  }
3776
- var PERCENT_MAX = 100;
3777
- var ICON_RADIUS = 10;
3778
- var ICON_VIEWBOX = 24;
3779
- var ICON_CENTER = 12;
3780
- var ICON_STROKE_WIDTH = 2;
3781
- var ContextContext = createContext(null);
3782
- var useContextValue = () => {
3783
- const context = useContext(ContextContext);
3784
- if (!context) {
3785
- throw new Error("Context components must be used within Context");
3786
- }
3787
- return context;
3788
- };
3789
- var Context = ({
3790
- usedTokens,
3791
- maxTokens,
3792
- usage,
3793
- modelId,
3850
+ function CommandList({
3851
+ className,
3794
3852
  ...props
3795
- }) => {
3796
- const contextValue = useMemo(
3797
- () => ({ maxTokens, modelId, usage, usedTokens }),
3798
- [maxTokens, modelId, usage, usedTokens]
3853
+ }) {
3854
+ return /* @__PURE__ */ jsx(
3855
+ Command$1.List,
3856
+ {
3857
+ "data-slot": "command-list",
3858
+ className: cn(
3859
+ "no-scrollbar max-h-72 scroll-py-1 overflow-x-hidden overflow-y-auto outline-none",
3860
+ className
3861
+ ),
3862
+ ...props
3863
+ }
3799
3864
  );
3800
- return /* @__PURE__ */ jsx(ContextContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(HoverCard, { closeDelay: 0, openDelay: 0, ...props }) });
3801
- };
3802
- var ContextIcon = () => {
3803
- const { usedTokens, maxTokens } = useContextValue();
3804
- const circumference = 2 * Math.PI * ICON_RADIUS;
3805
- const usedPercent = usedTokens / maxTokens;
3806
- const dashOffset = circumference * (1 - usedPercent);
3807
- return /* @__PURE__ */ jsxs(
3808
- "svg",
3865
+ }
3866
+ function CommandEmpty({
3867
+ className,
3868
+ ...props
3869
+ }) {
3870
+ return /* @__PURE__ */ jsx(
3871
+ Command$1.Empty,
3809
3872
  {
3810
- "aria-label": "Model context usage",
3811
- height: "20",
3812
- role: "img",
3813
- style: { color: "currentcolor" },
3814
- viewBox: `0 0 ${ICON_VIEWBOX} ${ICON_VIEWBOX}`,
3815
- width: "20",
3816
- children: [
3817
- /* @__PURE__ */ jsx(
3818
- "circle",
3819
- {
3820
- cx: ICON_CENTER,
3821
- cy: ICON_CENTER,
3822
- fill: "none",
3823
- opacity: "0.25",
3824
- r: ICON_RADIUS,
3825
- stroke: "currentColor",
3826
- strokeWidth: ICON_STROKE_WIDTH
3827
- }
3828
- ),
3829
- /* @__PURE__ */ jsx(
3830
- "circle",
3831
- {
3832
- cx: ICON_CENTER,
3833
- cy: ICON_CENTER,
3834
- fill: "none",
3835
- opacity: "0.7",
3836
- r: ICON_RADIUS,
3837
- stroke: "currentColor",
3838
- strokeDasharray: `${circumference} ${circumference}`,
3839
- strokeDashoffset: dashOffset,
3840
- strokeLinecap: "round",
3841
- strokeWidth: ICON_STROKE_WIDTH,
3842
- style: { transform: "rotate(-90deg)", transformOrigin: "center" }
3843
- }
3844
- )
3845
- ]
3873
+ "data-slot": "command-empty",
3874
+ className: cn("py-6 text-center text-sm", className),
3875
+ ...props
3846
3876
  }
3847
3877
  );
3848
- };
3849
- var ContextTrigger = ({ children, ...props }) => {
3850
- return /* @__PURE__ */ jsx(HoverCardTrigger, { asChild: true, children: children ?? /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", ...props, children: /* @__PURE__ */ jsx(ContextIcon, {}) }) });
3851
- };
3852
- var ContextContent = ({
3878
+ }
3879
+ function CommandGroup({
3853
3880
  className,
3854
3881
  ...props
3855
- }) => /* @__PURE__ */ jsx(
3856
- HoverCardContent,
3857
- {
3858
- className: cn("min-w-60 divide-y overflow-hidden p-0", className),
3859
- ...props
3860
- }
3861
- );
3862
- var ContextContentHeader = ({
3863
- children,
3864
- className,
3865
- ...props
3866
- }) => {
3867
- const { usedTokens, maxTokens } = useContextValue();
3868
- const usedPercent = usedTokens / maxTokens;
3869
- const displayPct = new Intl.NumberFormat("en-US", {
3870
- maximumFractionDigits: 1,
3871
- style: "percent"
3872
- }).format(usedPercent);
3873
- const used = new Intl.NumberFormat("en-US", {
3874
- notation: "compact"
3875
- }).format(usedTokens);
3876
- const total = new Intl.NumberFormat("en-US", {
3877
- notation: "compact"
3878
- }).format(maxTokens);
3879
- return /* @__PURE__ */ jsx("div", { className: cn("w-full space-y-2 p-3", className), ...props, children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
3880
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 text-xs", children: [
3881
- /* @__PURE__ */ jsx("p", { children: displayPct }),
3882
- /* @__PURE__ */ jsxs("p", { className: " text-muted-foreground", children: [
3883
- used,
3884
- " / ",
3885
- total
3886
- ] })
3887
- ] }),
3888
- /* @__PURE__ */ jsx("div", { className: "space-y-2", children: /* @__PURE__ */ jsx(Progress, { className: "bg-muted", value: usedPercent * PERCENT_MAX }) })
3889
- ] }) });
3890
- };
3891
- var ContextContentBody = ({
3892
- children,
3893
- className,
3894
- ...props
3895
- }) => /* @__PURE__ */ jsx("div", { className: cn("w-full p-3", className), ...props, children });
3896
- var TokensWithCost = ({
3897
- tokens,
3898
- costText
3899
- }) => /* @__PURE__ */ jsxs("span", { children: [
3900
- tokens === void 0 ? "\u2014" : new Intl.NumberFormat("en-US", {
3901
- notation: "compact"
3902
- }).format(tokens),
3903
- costText ? /* @__PURE__ */ jsxs("span", { className: "ml-2 text-muted-foreground", children: [
3904
- "\u2022 ",
3905
- costText
3906
- ] }) : null
3907
- ] });
3908
- var ContextInputUsage = ({
3909
- className,
3910
- children,
3911
- ...props
3912
- }) => {
3913
- const { usage, modelId } = useContextValue();
3914
- const inputTokens = usage?.inputTokens ?? 0;
3915
- if (children) {
3916
- return children;
3917
- }
3918
- if (!inputTokens) {
3919
- return null;
3920
- }
3921
- const inputCost = modelId ? getUsage({
3922
- modelId,
3923
- usage: { input: inputTokens, output: 0 }
3924
- }).costUSD?.totalUSD : void 0;
3925
- const inputCostText = new Intl.NumberFormat("en-US", {
3926
- currency: "USD",
3927
- style: "currency"
3928
- }).format(inputCost ?? 0);
3929
- return /* @__PURE__ */ jsxs(
3930
- "div",
3931
- {
3932
- className: cn("flex items-center justify-between text-xs", className),
3933
- ...props,
3934
- children: [
3935
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Input" }),
3936
- /* @__PURE__ */ jsx(TokensWithCost, { costText: inputCostText, tokens: inputTokens })
3937
- ]
3938
- }
3939
- );
3940
- };
3941
- var ContextOutputUsage = ({
3942
- className,
3943
- children,
3944
- ...props
3945
- }) => {
3946
- const { usage, modelId } = useContextValue();
3947
- const outputTokens = usage?.outputTokens ?? 0;
3948
- if (children) {
3949
- return children;
3950
- }
3951
- if (!outputTokens) {
3952
- return null;
3953
- }
3954
- const outputCost = modelId ? getUsage({
3955
- modelId,
3956
- usage: { input: 0, output: outputTokens }
3957
- }).costUSD?.totalUSD : void 0;
3958
- const outputCostText = new Intl.NumberFormat("en-US", {
3959
- currency: "USD",
3960
- style: "currency"
3961
- }).format(outputCost ?? 0);
3962
- return /* @__PURE__ */ jsxs(
3963
- "div",
3964
- {
3965
- className: cn("flex items-center justify-between text-xs", className),
3966
- ...props,
3967
- children: [
3968
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Output" }),
3969
- /* @__PURE__ */ jsx(TokensWithCost, { costText: outputCostText, tokens: outputTokens })
3970
- ]
3971
- }
3972
- );
3973
- };
3974
- var ContextReasoningUsage = ({
3975
- className,
3976
- children,
3977
- ...props
3978
- }) => {
3979
- const { usage, modelId } = useContextValue();
3980
- const reasoningTokens = usage?.reasoningTokens ?? 0;
3981
- if (children) {
3982
- return children;
3983
- }
3984
- if (!reasoningTokens) {
3985
- return null;
3986
- }
3987
- const reasoningCost = modelId ? getUsage({
3988
- modelId,
3989
- usage: { reasoningTokens }
3990
- }).costUSD?.totalUSD : void 0;
3991
- const reasoningCostText = new Intl.NumberFormat("en-US", {
3992
- currency: "USD",
3993
- style: "currency"
3994
- }).format(reasoningCost ?? 0);
3995
- return /* @__PURE__ */ jsxs(
3996
- "div",
3997
- {
3998
- className: cn("flex items-center justify-between text-xs", className),
3999
- ...props,
4000
- children: [
4001
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Reasoning" }),
4002
- /* @__PURE__ */ jsx(TokensWithCost, { costText: reasoningCostText, tokens: reasoningTokens })
4003
- ]
4004
- }
4005
- );
4006
- };
4007
- var ContextCacheUsage = ({
4008
- className,
4009
- children,
4010
- ...props
4011
- }) => {
4012
- const { usage, modelId } = useContextValue();
4013
- const cacheTokens = usage?.cachedInputTokens ?? 0;
4014
- if (children) {
4015
- return children;
4016
- }
4017
- if (!cacheTokens) {
4018
- return null;
4019
- }
4020
- const cacheCost = modelId ? getUsage({
4021
- modelId,
4022
- usage: { cacheReads: cacheTokens, input: 0, output: 0 }
4023
- }).costUSD?.totalUSD : void 0;
4024
- const cacheCostText = new Intl.NumberFormat("en-US", {
4025
- currency: "USD",
4026
- style: "currency"
4027
- }).format(cacheCost ?? 0);
4028
- return /* @__PURE__ */ jsxs(
4029
- "div",
4030
- {
4031
- className: cn("flex items-center justify-between text-xs", className),
4032
- ...props,
4033
- children: [
4034
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Cache" }),
4035
- /* @__PURE__ */ jsx(TokensWithCost, { costText: cacheCostText, tokens: cacheTokens })
4036
- ]
4037
- }
4038
- );
4039
- };
4040
- function InputGroup({ className, ...props }) {
4041
- return /* @__PURE__ */ jsx(
4042
- "div",
4043
- {
4044
- "data-slot": "input-group",
4045
- role: "group",
4046
- className: cn(
4047
- "group/input-group relative flex h-10 w-full min-w-0 items-center rounded-none border border-transparent border-b-input bg-transparent transition-[color,border-color] outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-data-[align=block-end]:rounded-none has-data-[align=block-start]:rounded-none has-[[data-slot=input-group-control]:focus-visible]:border-b-ring has-[[data-slot][aria-invalid=true]]:border-b-destructive has-[textarea]:rounded-none has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:has-[[data-slot][aria-invalid=true]]:border-b-destructive/50 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3",
4048
- className
4049
- ),
4050
- ...props
4051
- }
4052
- );
4053
- }
4054
- var inputGroupAddonVariants = cva(
4055
- "flex h-auto cursor-text items-center justify-center gap-2 py-2 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 **:data-[slot=kbd]:rounded-none **:data-[slot=kbd]:bg-muted-foreground/10 **:data-[slot=kbd]:px-1.5 [&>svg:not([class*='size-'])]:size-3.5",
4056
- {
4057
- variants: {
4058
- align: {
4059
- "inline-start": "order-first",
4060
- "inline-end": "order-last",
4061
- "block-start": "order-first w-full justify-start pt-3 group-has-[>input]/input-group:pt-3.5 [.border-b]:pb-3.5",
4062
- "block-end": "order-last w-full justify-start pb-3 group-has-[>input]/input-group:pb-3.5 [.border-t]:pt-3.5"
4063
- }
4064
- },
4065
- defaultVariants: {
4066
- align: "inline-start"
4067
- }
4068
- }
4069
- );
4070
- function InputGroupAddon({
4071
- className,
4072
- align = "inline-start",
4073
- ...props
4074
- }) {
4075
- return /* @__PURE__ */ jsx(
4076
- "div",
4077
- {
4078
- role: "group",
4079
- "data-slot": "input-group-addon",
4080
- "data-align": align,
4081
- className: cn(inputGroupAddonVariants({ align }), className),
4082
- onClick: (e) => {
4083
- if (e.target.closest("button")) {
4084
- return;
4085
- }
4086
- e.currentTarget.parentElement?.querySelector("input")?.focus();
4087
- },
4088
- ...props
4089
- }
4090
- );
4091
- }
4092
- var inputGroupButtonVariants = cva(
4093
- "flex items-center gap-2 rounded-none text-sm shadow-none",
4094
- {
4095
- variants: {
4096
- size: {
4097
- xs: "h-6 gap-1 rounded-none px-1.5 text-xs [&>svg:not([class*='size-'])]:size-3.5",
4098
- sm: "",
4099
- "icon-xs": "size-6 p-0 text-xs has-[>svg]:p-0",
4100
- "icon-sm": "size-8 p-0 has-[>svg]:p-0"
4101
- }
4102
- },
4103
- defaultVariants: {
4104
- size: "xs"
4105
- }
4106
- }
4107
- );
4108
- function InputGroupButton({
4109
- className,
4110
- type = "button",
4111
- variant = "ghost",
4112
- size = "xs",
4113
- ...props
4114
- }) {
4115
- return /* @__PURE__ */ jsx(
4116
- Button,
4117
- {
4118
- type,
4119
- "data-size": size,
4120
- variant,
4121
- className: cn(inputGroupButtonVariants({ size }), className),
4122
- ...props
4123
- }
4124
- );
4125
- }
4126
- function InputGroupTextarea({
4127
- className,
4128
- ...props
4129
- }) {
4130
- return /* @__PURE__ */ jsx(
4131
- Textarea,
4132
- {
4133
- "data-slot": "input-group-control",
4134
- className: cn(
4135
- "flex-1 resize-none border-0 bg-transparent py-2.5 ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent",
4136
- className
4137
- ),
4138
- ...props
4139
- }
4140
- );
4141
- }
4142
- function Command({
3882
+ }) {
3883
+ return /* @__PURE__ */ jsx(
3884
+ Command$1.Group,
3885
+ {
3886
+ "data-slot": "command-group",
3887
+ className: cn(
3888
+ "overflow-hidden p-1.5 text-foreground **:[[cmdk-group-heading]]:px-3 **:[[cmdk-group-heading]]:py-2 **:[[cmdk-group-heading]]:text-xs **:[[cmdk-group-heading]]:font-semibold **:[[cmdk-group-heading]]:tracking-wider **:[[cmdk-group-heading]]:text-muted-foreground **:[[cmdk-group-heading]]:uppercase",
3889
+ className
3890
+ ),
3891
+ ...props
3892
+ }
3893
+ );
3894
+ }
3895
+ function CommandItem({
4143
3896
  className,
4144
- ...props
4145
- }) {
4146
- return /* @__PURE__ */ jsx(
4147
- Command$1,
4148
- {
4149
- "data-slot": "command",
4150
- className: cn(
4151
- "flex size-full flex-col overflow-hidden bg-popover text-popover-foreground",
4152
- className
4153
- ),
4154
- ...props
4155
- }
4156
- );
4157
- }
4158
- function CommandList({
4159
- className,
4160
- ...props
4161
- }) {
4162
- return /* @__PURE__ */ jsx(
4163
- Command$1.List,
4164
- {
4165
- "data-slot": "command-list",
4166
- className: cn(
4167
- "no-scrollbar max-h-72 scroll-py-1 overflow-x-hidden overflow-y-auto outline-none",
4168
- className
4169
- ),
4170
- ...props
4171
- }
4172
- );
4173
- }
4174
- function CommandEmpty({
4175
- className,
4176
- ...props
4177
- }) {
4178
- return /* @__PURE__ */ jsx(
4179
- Command$1.Empty,
4180
- {
4181
- "data-slot": "command-empty",
4182
- className: cn("py-6 text-center text-sm", className),
4183
- ...props
4184
- }
4185
- );
4186
- }
4187
- function CommandGroup({
4188
- className,
4189
- ...props
4190
- }) {
4191
- return /* @__PURE__ */ jsx(
4192
- Command$1.Group,
4193
- {
4194
- "data-slot": "command-group",
4195
- className: cn(
4196
- "overflow-hidden p-1.5 text-foreground **:[[cmdk-group-heading]]:px-3 **:[[cmdk-group-heading]]:py-2 **:[[cmdk-group-heading]]:text-xs **:[[cmdk-group-heading]]:font-semibold **:[[cmdk-group-heading]]:tracking-wider **:[[cmdk-group-heading]]:text-muted-foreground **:[[cmdk-group-heading]]:uppercase",
4197
- className
4198
- ),
4199
- ...props
4200
- }
4201
- );
4202
- }
4203
- function CommandItem({
4204
- className,
4205
- children,
3897
+ children,
4206
3898
  ...props
4207
3899
  }) {
4208
3900
  return /* @__PURE__ */ jsxs(
@@ -4267,12 +3959,42 @@ function DropdownMenuItem({
4267
3959
  "data-inset": inset,
4268
3960
  "data-variant": variant,
4269
3961
  className: cn(
4270
- "group/dropdown-menu-item relative flex cursor-default items-center gap-2.5 rounded-none px-3 py-2 text-xs font-medium tracking-wider uppercase outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-9.5 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5 data-[variant=destructive]:*:[svg]:text-destructive",
3962
+ "group/dropdown-menu-item relative flex cursor-default items-center gap-2.5 rounded-none px-3 py-2 text-xs font-medium tracking-wider uppercase outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-9.5 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5 data-[variant=destructive]:*:[svg]:text-destructive",
3963
+ className
3964
+ ),
3965
+ ...props
3966
+ }
3967
+ );
3968
+ }
3969
+ function HoverCard({
3970
+ ...props
3971
+ }) {
3972
+ return /* @__PURE__ */ jsx(HoverCard$1.Root, { "data-slot": "hover-card", ...props });
3973
+ }
3974
+ function HoverCardTrigger({
3975
+ ...props
3976
+ }) {
3977
+ return /* @__PURE__ */ jsx(HoverCard$1.Trigger, { "data-slot": "hover-card-trigger", ...props });
3978
+ }
3979
+ function HoverCardContent({
3980
+ className,
3981
+ align = "center",
3982
+ sideOffset = 4,
3983
+ ...props
3984
+ }) {
3985
+ return /* @__PURE__ */ jsx(HoverCard$1.Portal, { "data-slot": "hover-card-portal", children: /* @__PURE__ */ jsx(
3986
+ HoverCard$1.Content,
3987
+ {
3988
+ "data-slot": "hover-card-content",
3989
+ align,
3990
+ sideOffset,
3991
+ className: cn(
3992
+ "z-50 w-72 origin-(--radix-hover-card-content-transform-origin) bg-popover p-4 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
4271
3993
  className
4272
3994
  ),
4273
3995
  ...props
4274
3996
  }
4275
- );
3997
+ ) });
4276
3998
  }
4277
3999
  function Spinner({ className, ...props }) {
4278
4000
  return /* @__PURE__ */ jsx(Loader2Icon, { role: "status", "aria-label": "Loading", className: cn("size-4 animate-spin", className), ...props });
@@ -4307,6 +4029,15 @@ var usePromptInputController = () => {
4307
4029
  return ctx;
4308
4030
  };
4309
4031
  var useOptionalPromptInputController = () => useContext(PromptInputController);
4032
+ var useProviderAttachments = () => {
4033
+ const ctx = useContext(ProviderAttachmentsContext);
4034
+ if (!ctx) {
4035
+ throw new Error(
4036
+ "Wrap your component inside <PromptInputProvider> to use useProviderAttachments()."
4037
+ );
4038
+ }
4039
+ return ctx;
4040
+ };
4310
4041
  var useOptionalProviderAttachments = () => useContext(ProviderAttachmentsContext);
4311
4042
  var PromptInputProvider = ({
4312
4043
  initialInput: initialTextInput = "",
@@ -4867,110 +4598,399 @@ var PromptInputTextarea = ({
4867
4598
  }
4868
4599
  );
4869
4600
  };
4870
- var PromptInputFooter = ({
4601
+ var PromptInputFooter = ({
4602
+ className,
4603
+ ...props
4604
+ }) => /* @__PURE__ */ jsx(
4605
+ InputGroupAddon,
4606
+ {
4607
+ align: "block-end",
4608
+ className: cn("justify-between gap-1", className),
4609
+ ...props
4610
+ }
4611
+ );
4612
+ var PromptInputTools = ({
4613
+ className,
4614
+ ...props
4615
+ }) => /* @__PURE__ */ jsx(
4616
+ "div",
4617
+ {
4618
+ className: cn("flex min-w-0 items-center gap-1", className),
4619
+ ...props
4620
+ }
4621
+ );
4622
+ var PromptInputButton = ({
4623
+ variant = "ghost",
4624
+ className,
4625
+ size,
4626
+ tooltip,
4627
+ ...props
4628
+ }) => {
4629
+ const newSize = size ?? (Children.count(props.children) > 1 ? "sm" : "icon-sm");
4630
+ const button = /* @__PURE__ */ jsx(
4631
+ InputGroupButton,
4632
+ {
4633
+ className: cn(className),
4634
+ size: newSize,
4635
+ type: "button",
4636
+ variant,
4637
+ ...props
4638
+ }
4639
+ );
4640
+ if (!tooltip) {
4641
+ return button;
4642
+ }
4643
+ const tooltipContent = typeof tooltip === "string" ? tooltip : tooltip.content;
4644
+ const shortcut = typeof tooltip === "string" ? void 0 : tooltip.shortcut;
4645
+ const side = typeof tooltip === "string" ? "top" : tooltip.side ?? "top";
4646
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [
4647
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: button }),
4648
+ /* @__PURE__ */ jsxs(TooltipContent, { side, children: [
4649
+ tooltipContent,
4650
+ shortcut && /* @__PURE__ */ jsx("span", { className: "ml-2 text-muted-foreground", children: shortcut })
4651
+ ] })
4652
+ ] });
4653
+ };
4654
+ var PromptInputActionMenu = (props) => /* @__PURE__ */ jsx(DropdownMenu, { ...props });
4655
+ var PromptInputActionMenuTrigger = ({
4656
+ className,
4657
+ children,
4658
+ ...props
4659
+ }) => /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(PromptInputButton, { className, ...props, children: children ?? /* @__PURE__ */ jsx(PlusIcon, { className: "size-4" }) }) });
4660
+ var PromptInputActionMenuContent = ({
4661
+ className,
4662
+ ...props
4663
+ }) => /* @__PURE__ */ jsx(DropdownMenuContent, { align: "start", className: cn(className), ...props });
4664
+ var PromptInputSubmit = ({
4665
+ className,
4666
+ variant = "default",
4667
+ size = "icon-sm",
4668
+ status,
4669
+ onStop,
4670
+ onClick,
4671
+ children,
4672
+ ...props
4673
+ }) => {
4674
+ const isGenerating = status === "submitted" || status === "streaming";
4675
+ let Icon = /* @__PURE__ */ jsx(CornerDownLeftIcon, { className: "size-4" });
4676
+ if (status === "submitted") {
4677
+ Icon = /* @__PURE__ */ jsx(Spinner, {});
4678
+ } else if (status === "streaming") {
4679
+ Icon = /* @__PURE__ */ jsx(SquareIcon, { className: "size-4" });
4680
+ } else if (status === "error") {
4681
+ Icon = /* @__PURE__ */ jsx(XIcon, { className: "size-4" });
4682
+ }
4683
+ const handleClick = useCallback(
4684
+ (e) => {
4685
+ if (isGenerating && onStop) {
4686
+ e.preventDefault();
4687
+ onStop();
4688
+ return;
4689
+ }
4690
+ onClick?.(e);
4691
+ },
4692
+ [isGenerating, onStop, onClick]
4693
+ );
4694
+ return /* @__PURE__ */ jsx(
4695
+ InputGroupButton,
4696
+ {
4697
+ "aria-label": isGenerating ? "Stop" : "Submit",
4698
+ className: cn(className),
4699
+ onClick: handleClick,
4700
+ size,
4701
+ type: isGenerating && onStop ? "button" : "submit",
4702
+ variant,
4703
+ ...props,
4704
+ children: children ?? Icon
4705
+ }
4706
+ );
4707
+ };
4708
+ function Progress({
4709
+ className,
4710
+ value,
4711
+ ...props
4712
+ }) {
4713
+ return /* @__PURE__ */ jsx(
4714
+ Progress$1.Root,
4715
+ {
4716
+ "data-slot": "progress",
4717
+ className: cn(
4718
+ "relative flex h-0.5 w-full items-center overflow-x-hidden rounded-none bg-muted",
4719
+ className
4720
+ ),
4721
+ ...props,
4722
+ children: /* @__PURE__ */ jsx(
4723
+ Progress$1.Indicator,
4724
+ {
4725
+ "data-slot": "progress-indicator",
4726
+ className: "size-full flex-1 bg-primary transition-all",
4727
+ style: { transform: `translateX(-${100 - (value || 0)}%)` }
4728
+ }
4729
+ )
4730
+ }
4731
+ );
4732
+ }
4733
+ var PERCENT_MAX = 100;
4734
+ var ICON_RADIUS = 10;
4735
+ var ICON_VIEWBOX = 24;
4736
+ var ICON_CENTER = 12;
4737
+ var ICON_STROKE_WIDTH = 2;
4738
+ var ContextContext = createContext(null);
4739
+ var useContextValue = () => {
4740
+ const context = useContext(ContextContext);
4741
+ if (!context) {
4742
+ throw new Error("Context components must be used within Context");
4743
+ }
4744
+ return context;
4745
+ };
4746
+ var Context = ({
4747
+ usedTokens,
4748
+ maxTokens,
4749
+ usage,
4750
+ modelId,
4751
+ ...props
4752
+ }) => {
4753
+ const contextValue = useMemo(
4754
+ () => ({ maxTokens, modelId, usage, usedTokens }),
4755
+ [maxTokens, modelId, usage, usedTokens]
4756
+ );
4757
+ return /* @__PURE__ */ jsx(ContextContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(HoverCard, { closeDelay: 0, openDelay: 0, ...props }) });
4758
+ };
4759
+ var ContextIcon = () => {
4760
+ const { usedTokens, maxTokens } = useContextValue();
4761
+ const circumference = 2 * Math.PI * ICON_RADIUS;
4762
+ const usedPercent = usedTokens / maxTokens;
4763
+ const dashOffset = circumference * (1 - usedPercent);
4764
+ return /* @__PURE__ */ jsxs(
4765
+ "svg",
4766
+ {
4767
+ "aria-label": "Model context usage",
4768
+ height: "20",
4769
+ role: "img",
4770
+ style: { color: "currentcolor" },
4771
+ viewBox: `0 0 ${ICON_VIEWBOX} ${ICON_VIEWBOX}`,
4772
+ width: "20",
4773
+ children: [
4774
+ /* @__PURE__ */ jsx(
4775
+ "circle",
4776
+ {
4777
+ cx: ICON_CENTER,
4778
+ cy: ICON_CENTER,
4779
+ fill: "none",
4780
+ opacity: "0.25",
4781
+ r: ICON_RADIUS,
4782
+ stroke: "currentColor",
4783
+ strokeWidth: ICON_STROKE_WIDTH
4784
+ }
4785
+ ),
4786
+ /* @__PURE__ */ jsx(
4787
+ "circle",
4788
+ {
4789
+ cx: ICON_CENTER,
4790
+ cy: ICON_CENTER,
4791
+ fill: "none",
4792
+ opacity: "0.7",
4793
+ r: ICON_RADIUS,
4794
+ stroke: "currentColor",
4795
+ strokeDasharray: `${circumference} ${circumference}`,
4796
+ strokeDashoffset: dashOffset,
4797
+ strokeLinecap: "round",
4798
+ strokeWidth: ICON_STROKE_WIDTH,
4799
+ style: { transform: "rotate(-90deg)", transformOrigin: "center" }
4800
+ }
4801
+ )
4802
+ ]
4803
+ }
4804
+ );
4805
+ };
4806
+ var ContextTrigger = ({ children, ...props }) => {
4807
+ return /* @__PURE__ */ jsx(HoverCardTrigger, { asChild: true, children: children ?? /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", ...props, children: /* @__PURE__ */ jsx(ContextIcon, {}) }) });
4808
+ };
4809
+ var ContextContent = ({
4810
+ className,
4811
+ ...props
4812
+ }) => /* @__PURE__ */ jsx(
4813
+ HoverCardContent,
4814
+ {
4815
+ className: cn("min-w-60 divide-y overflow-hidden p-0", className),
4816
+ ...props
4817
+ }
4818
+ );
4819
+ var ContextContentHeader = ({
4820
+ children,
4821
+ className,
4822
+ ...props
4823
+ }) => {
4824
+ const { usedTokens, maxTokens } = useContextValue();
4825
+ const usedPercent = usedTokens / maxTokens;
4826
+ const displayPct = new Intl.NumberFormat("en-US", {
4827
+ maximumFractionDigits: 1,
4828
+ style: "percent"
4829
+ }).format(usedPercent);
4830
+ const used = new Intl.NumberFormat("en-US", {
4831
+ notation: "compact"
4832
+ }).format(usedTokens);
4833
+ const total = new Intl.NumberFormat("en-US", {
4834
+ notation: "compact"
4835
+ }).format(maxTokens);
4836
+ return /* @__PURE__ */ jsx("div", { className: cn("w-full space-y-2 p-3", className), ...props, children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
4837
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 text-xs", children: [
4838
+ /* @__PURE__ */ jsx("p", { children: displayPct }),
4839
+ /* @__PURE__ */ jsxs("p", { className: " text-muted-foreground", children: [
4840
+ used,
4841
+ " / ",
4842
+ total
4843
+ ] })
4844
+ ] }),
4845
+ /* @__PURE__ */ jsx("div", { className: "space-y-2", children: /* @__PURE__ */ jsx(Progress, { className: "bg-muted", value: usedPercent * PERCENT_MAX }) })
4846
+ ] }) });
4847
+ };
4848
+ var ContextContentBody = ({
4849
+ children,
4871
4850
  className,
4872
4851
  ...props
4873
- }) => /* @__PURE__ */ jsx(
4874
- InputGroupAddon,
4875
- {
4876
- align: "block-end",
4877
- className: cn("justify-between gap-1", className),
4878
- ...props
4879
- }
4880
- );
4881
- var PromptInputTools = ({
4852
+ }) => /* @__PURE__ */ jsx("div", { className: cn("w-full p-3", className), ...props, children });
4853
+ var TokensWithCost = ({
4854
+ tokens,
4855
+ costText
4856
+ }) => /* @__PURE__ */ jsxs("span", { children: [
4857
+ tokens === void 0 ? "\u2014" : new Intl.NumberFormat("en-US", {
4858
+ notation: "compact"
4859
+ }).format(tokens),
4860
+ costText ? /* @__PURE__ */ jsxs("span", { className: "ml-2 text-muted-foreground", children: [
4861
+ "\u2022 ",
4862
+ costText
4863
+ ] }) : null
4864
+ ] });
4865
+ var ContextInputUsage = ({
4882
4866
  className,
4867
+ children,
4883
4868
  ...props
4884
- }) => /* @__PURE__ */ jsx(
4885
- "div",
4886
- {
4887
- className: cn("flex min-w-0 items-center gap-1", className),
4888
- ...props
4869
+ }) => {
4870
+ const { usage, modelId } = useContextValue();
4871
+ const inputTokens = usage?.inputTokens ?? 0;
4872
+ if (children) {
4873
+ return children;
4889
4874
  }
4890
- );
4891
- var PromptInputButton = ({
4892
- variant = "ghost",
4875
+ if (!inputTokens) {
4876
+ return null;
4877
+ }
4878
+ const inputCost = modelId ? getUsage({
4879
+ modelId,
4880
+ usage: { input: inputTokens, output: 0 }
4881
+ }).costUSD?.totalUSD : void 0;
4882
+ const inputCostText = new Intl.NumberFormat("en-US", {
4883
+ currency: "USD",
4884
+ style: "currency"
4885
+ }).format(inputCost ?? 0);
4886
+ return /* @__PURE__ */ jsxs(
4887
+ "div",
4888
+ {
4889
+ className: cn("flex items-center justify-between text-xs", className),
4890
+ ...props,
4891
+ children: [
4892
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Input" }),
4893
+ /* @__PURE__ */ jsx(TokensWithCost, { costText: inputCostText, tokens: inputTokens })
4894
+ ]
4895
+ }
4896
+ );
4897
+ };
4898
+ var ContextOutputUsage = ({
4893
4899
  className,
4894
- size,
4895
- tooltip,
4900
+ children,
4896
4901
  ...props
4897
4902
  }) => {
4898
- const newSize = size ?? (Children.count(props.children) > 1 ? "sm" : "icon-sm");
4899
- const button = /* @__PURE__ */ jsx(
4900
- InputGroupButton,
4903
+ const { usage, modelId } = useContextValue();
4904
+ const outputTokens = usage?.outputTokens ?? 0;
4905
+ if (children) {
4906
+ return children;
4907
+ }
4908
+ if (!outputTokens) {
4909
+ return null;
4910
+ }
4911
+ const outputCost = modelId ? getUsage({
4912
+ modelId,
4913
+ usage: { input: 0, output: outputTokens }
4914
+ }).costUSD?.totalUSD : void 0;
4915
+ const outputCostText = new Intl.NumberFormat("en-US", {
4916
+ currency: "USD",
4917
+ style: "currency"
4918
+ }).format(outputCost ?? 0);
4919
+ return /* @__PURE__ */ jsxs(
4920
+ "div",
4901
4921
  {
4902
- className: cn(className),
4903
- size: newSize,
4904
- type: "button",
4905
- variant,
4906
- ...props
4922
+ className: cn("flex items-center justify-between text-xs", className),
4923
+ ...props,
4924
+ children: [
4925
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Output" }),
4926
+ /* @__PURE__ */ jsx(TokensWithCost, { costText: outputCostText, tokens: outputTokens })
4927
+ ]
4907
4928
  }
4908
4929
  );
4909
- if (!tooltip) {
4910
- return button;
4911
- }
4912
- const tooltipContent = typeof tooltip === "string" ? tooltip : tooltip.content;
4913
- const shortcut = typeof tooltip === "string" ? void 0 : tooltip.shortcut;
4914
- const side = typeof tooltip === "string" ? "top" : tooltip.side ?? "top";
4915
- return /* @__PURE__ */ jsxs(Tooltip, { children: [
4916
- /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: button }),
4917
- /* @__PURE__ */ jsxs(TooltipContent, { side, children: [
4918
- tooltipContent,
4919
- shortcut && /* @__PURE__ */ jsx("span", { className: "ml-2 text-muted-foreground", children: shortcut })
4920
- ] })
4921
- ] });
4922
4930
  };
4923
- var PromptInputActionMenu = (props) => /* @__PURE__ */ jsx(DropdownMenu, { ...props });
4924
- var PromptInputActionMenuTrigger = ({
4931
+ var ContextReasoningUsage = ({
4925
4932
  className,
4926
4933
  children,
4927
4934
  ...props
4928
- }) => /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(PromptInputButton, { className, ...props, children: children ?? /* @__PURE__ */ jsx(PlusIcon, { className: "size-4" }) }) });
4929
- var PromptInputActionMenuContent = ({
4930
- className,
4931
- ...props
4932
- }) => /* @__PURE__ */ jsx(DropdownMenuContent, { align: "start", className: cn(className), ...props });
4933
- var PromptInputSubmit = ({
4935
+ }) => {
4936
+ const { usage, modelId } = useContextValue();
4937
+ const reasoningTokens = usage?.reasoningTokens ?? 0;
4938
+ if (children) {
4939
+ return children;
4940
+ }
4941
+ if (!reasoningTokens) {
4942
+ return null;
4943
+ }
4944
+ const reasoningCost = modelId ? getUsage({
4945
+ modelId,
4946
+ usage: { reasoningTokens }
4947
+ }).costUSD?.totalUSD : void 0;
4948
+ const reasoningCostText = new Intl.NumberFormat("en-US", {
4949
+ currency: "USD",
4950
+ style: "currency"
4951
+ }).format(reasoningCost ?? 0);
4952
+ return /* @__PURE__ */ jsxs(
4953
+ "div",
4954
+ {
4955
+ className: cn("flex items-center justify-between text-xs", className),
4956
+ ...props,
4957
+ children: [
4958
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Reasoning" }),
4959
+ /* @__PURE__ */ jsx(TokensWithCost, { costText: reasoningCostText, tokens: reasoningTokens })
4960
+ ]
4961
+ }
4962
+ );
4963
+ };
4964
+ var ContextCacheUsage = ({
4934
4965
  className,
4935
- variant = "default",
4936
- size = "icon-sm",
4937
- status,
4938
- onStop,
4939
- onClick,
4940
4966
  children,
4941
4967
  ...props
4942
4968
  }) => {
4943
- const isGenerating = status === "submitted" || status === "streaming";
4944
- let Icon = /* @__PURE__ */ jsx(CornerDownLeftIcon, { className: "size-4" });
4945
- if (status === "submitted") {
4946
- Icon = /* @__PURE__ */ jsx(Spinner, {});
4947
- } else if (status === "streaming") {
4948
- Icon = /* @__PURE__ */ jsx(SquareIcon, { className: "size-4" });
4949
- } else if (status === "error") {
4950
- Icon = /* @__PURE__ */ jsx(XIcon, { className: "size-4" });
4969
+ const { usage, modelId } = useContextValue();
4970
+ const cacheTokens = usage?.cachedInputTokens ?? 0;
4971
+ if (children) {
4972
+ return children;
4951
4973
  }
4952
- const handleClick = useCallback(
4953
- (e) => {
4954
- if (isGenerating && onStop) {
4955
- e.preventDefault();
4956
- onStop();
4957
- return;
4958
- }
4959
- onClick?.(e);
4960
- },
4961
- [isGenerating, onStop, onClick]
4962
- );
4963
- return /* @__PURE__ */ jsx(
4964
- InputGroupButton,
4974
+ if (!cacheTokens) {
4975
+ return null;
4976
+ }
4977
+ const cacheCost = modelId ? getUsage({
4978
+ modelId,
4979
+ usage: { cacheReads: cacheTokens, input: 0, output: 0 }
4980
+ }).costUSD?.totalUSD : void 0;
4981
+ const cacheCostText = new Intl.NumberFormat("en-US", {
4982
+ currency: "USD",
4983
+ style: "currency"
4984
+ }).format(cacheCost ?? 0);
4985
+ return /* @__PURE__ */ jsxs(
4986
+ "div",
4965
4987
  {
4966
- "aria-label": isGenerating ? "Stop" : "Submit",
4967
- className: cn(className),
4968
- onClick: handleClick,
4969
- size,
4970
- type: isGenerating && onStop ? "button" : "submit",
4971
- variant,
4988
+ className: cn("flex items-center justify-between text-xs", className),
4972
4989
  ...props,
4973
- children: children ?? Icon
4990
+ children: [
4991
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Cache" }),
4992
+ /* @__PURE__ */ jsx(TokensWithCost, { costText: cacheCostText, tokens: cacheTokens })
4993
+ ]
4974
4994
  }
4975
4995
  );
4976
4996
  };
@@ -5007,6 +5027,29 @@ function PopoverAnchor({
5007
5027
  function ChatComposer(props) {
5008
5028
  return /* @__PURE__ */ jsx(PromptInputProvider, { children: /* @__PURE__ */ jsx(ChatComposerInner, { ...props }) });
5009
5029
  }
5030
+ function AttachmentPreviewStrip() {
5031
+ const attachments = useProviderAttachments();
5032
+ if (attachments.files.length === 0) return null;
5033
+ return /* @__PURE__ */ jsx("div", { className: "flex gap-2 px-3 pt-2 pb-1 flex-wrap", children: attachments.files.map((file) => /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
5034
+ file.mediaType?.startsWith("image/") && file.url ? /* @__PURE__ */ jsx(
5035
+ "img",
5036
+ {
5037
+ src: file.url,
5038
+ alt: file.filename,
5039
+ className: "h-16 w-16 rounded-lg border border-border object-cover"
5040
+ }
5041
+ ) : /* @__PURE__ */ jsx("div", { className: "h-16 w-16 rounded-lg border border-border bg-muted flex items-center justify-center text-[10px] text-muted-foreground truncate px-1", children: file.filename }),
5042
+ /* @__PURE__ */ jsx(
5043
+ "button",
5044
+ {
5045
+ type: "button",
5046
+ onClick: () => attachments.remove(file.id),
5047
+ className: "absolute -top-1.5 -right-1.5 hidden group-hover:flex items-center justify-center w-4 h-4 rounded-full bg-destructive text-destructive-foreground text-[10px]",
5048
+ children: "\xD7"
5049
+ }
5050
+ )
5051
+ ] }, file.id)) });
5052
+ }
5010
5053
  function ChatComposerInner({
5011
5054
  onSend,
5012
5055
  onStop,
@@ -5099,7 +5142,13 @@ function ChatComposerInner({
5099
5142
  (message) => {
5100
5143
  const text = message.text.trim();
5101
5144
  if (!text || isRunning || disabled) return;
5102
- onSend(text);
5145
+ const images = [];
5146
+ for (const file of message.files) {
5147
+ if (file.mediaType?.startsWith("image/") && file.url) {
5148
+ images.push({ data: file.url, mimeType: file.mediaType });
5149
+ }
5150
+ }
5151
+ onSend(text, images.length > 0 ? images : void 0);
5103
5152
  },
5104
5153
  [onSend, isRunning, disabled]
5105
5154
  );
@@ -5108,7 +5157,8 @@ function ChatComposerInner({
5108
5157
  setButtonMenuOpen(false);
5109
5158
  }
5110
5159
  }, children: [
5111
- /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs(PromptInput, { onSubmit: handleSubmit, children: [
5160
+ /* @__PURE__ */ jsx(AttachmentPreviewStrip, {}),
5161
+ /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs(PromptInput, { onSubmit: handleSubmit, accept: "image/*", multiple: true, children: [
5112
5162
  /* @__PURE__ */ jsx(PromptInputBody, { children: /* @__PURE__ */ jsx(
5113
5163
  PromptInputTextarea,
5114
5164
  {
@@ -5942,14 +5992,17 @@ function TimelineEntryItem({
5942
5992
  ] });
5943
5993
  }
5944
5994
  if (entry.kind === "tool") {
5945
- const failureSummary = effectiveStatus(entry.tc) === "error" ? toolErrorSummary(entry.tc.result) : "";
5995
+ const rawStatus = effectiveStatus(entry.tc);
5996
+ const hideArtifactFailure = rawStatus === "error" && entry.tc.toolName === "create_artifact";
5997
+ const indicatorStatus = hideArtifactFailure ? "running" : rawStatus;
5998
+ const failureSummary = rawStatus === "error" && !hideArtifactFailure ? toolErrorSummary(entry.tc.result) : "";
5946
5999
  return /* @__PURE__ */ jsxs(TimelineItem, { step, children: [
5947
6000
  /* @__PURE__ */ jsxs(TimelineHeader, { children: [
5948
6001
  /* @__PURE__ */ jsx(TimelineSeparator, { style: { backgroundColor: "var(--color-border)" } }),
5949
6002
  /* @__PURE__ */ jsx(
5950
6003
  TimelineIndicator,
5951
6004
  {
5952
- className: cn("size-2 border-none", statusColorClass(effectiveStatus(entry.tc)))
6005
+ className: cn("size-2 border-none", statusColorClass(indicatorStatus))
5953
6006
  }
5954
6007
  )
5955
6008
  ] }),
@@ -6007,13 +6060,14 @@ function TimelineEntryItem({
6007
6060
  /* @__PURE__ */ jsx(TimelineSeparator, { style: { backgroundColor: "var(--color-border)" } }),
6008
6061
  /* @__PURE__ */ jsx(TimelineIndicator, { className: "size-2 border-none bg-primary animate-pulse" })
6009
6062
  ] }),
6010
- /* @__PURE__ */ jsx(TimelineContent, { children: /* @__PURE__ */ jsx(Shimmer, { duration: 5, className: "text-sm text-foreground", children: "Working on it..." }) })
6063
+ /* @__PURE__ */ jsx(TimelineContent, { children: /* @__PURE__ */ jsx(Shimmer, { duration: 3, spread: 3, className: "text-sm", children: "Working on it..." }) })
6011
6064
  ] });
6012
6065
  }
6013
6066
  function AssistantGroup({
6014
6067
  messages,
6015
6068
  lastGlobalIndex,
6016
6069
  totalMessages,
6070
+ isRunning,
6017
6071
  sessionId,
6018
6072
  onFileSelect,
6019
6073
  onRetry
@@ -6023,6 +6077,15 @@ function AssistantGroup({
6023
6077
  const isLast = i === messages.length - 1 && lastGlobalIndex === totalMessages - 1;
6024
6078
  entries.push(...messageToEntries(messages[i], isLast));
6025
6079
  }
6080
+ const isTailGroup = lastGlobalIndex === totalMessages - 1;
6081
+ const lastEntry = entries[entries.length - 1];
6082
+ const hasRunningTool = entries.some(
6083
+ (e) => e.kind === "tool" && e.tc.status === "running"
6084
+ );
6085
+ const tailMsg = messages[messages.length - 1];
6086
+ if (isTailGroup && isRunning && !hasRunningTool && lastEntry?.kind !== "thinking") {
6087
+ entries.push({ kind: "thinking", key: `${tailMsg.id}-tail-thinking` });
6088
+ }
6026
6089
  const tail = messages[messages.length - 1];
6027
6090
  const showErrorInfo = tail && tail.role === "assistant" && tail.status === "error" && !!tail.errorInfo;
6028
6091
  return /* @__PURE__ */ jsx(Message, { from: "assistant", children: /* @__PURE__ */ jsxs(MessageContent, { children: [
@@ -6146,6 +6209,7 @@ function ChatThread({
6146
6209
  messages: group.messages,
6147
6210
  lastGlobalIndex: group.lastGlobalIndex,
6148
6211
  totalMessages: messages.length,
6212
+ isRunning,
6149
6213
  sessionId,
6150
6214
  onFileSelect,
6151
6215
  onRetry: groupRetry
@@ -6153,7 +6217,7 @@ function ChatThread({
6153
6217
  group.messages[0].id
6154
6218
  );
6155
6219
  }),
6156
- isRunning && messages.length > 0 && messages[messages.length - 1].role === "user" && /* @__PURE__ */ jsx(Message, { from: "assistant", children: /* @__PURE__ */ jsx(MessageContent, { children: /* @__PURE__ */ jsx(Shimmer, { duration: 5, className: "text-sm", children: "Working on it..." }) }) })
6220
+ isRunning && messages.length > 0 && messages[messages.length - 1].role === "user" && /* @__PURE__ */ jsx(Message, { from: "assistant", children: /* @__PURE__ */ jsx(MessageContent, { children: /* @__PURE__ */ jsx(Shimmer, { duration: 3, spread: 3, className: "text-sm", children: "Working on it..." }) }) })
6157
6221
  ] }) }),
6158
6222
  /* @__PURE__ */ jsx(ConversationScrollButton, {})
6159
6223
  ] }),
@@ -6842,6 +6906,7 @@ function WorkspacePanel({
6842
6906
  onSelectedPathChange,
6843
6907
  collapsed = false,
6844
6908
  onCollapsedChange,
6909
+ refreshSignal = 0,
6845
6910
  disabled = false
6846
6911
  }) {
6847
6912
  const fileInputRef = useRef(null);
@@ -6921,6 +6986,13 @@ function WorkspacePanel({
6921
6986
  useEffect(() => {
6922
6987
  void fetchTree();
6923
6988
  }, [fetchTree]);
6989
+ useEffect(() => {
6990
+ if (!sessionId || refreshSignal === 0) return;
6991
+ const timer = setTimeout(() => {
6992
+ void fetchTree();
6993
+ }, 300);
6994
+ return () => clearTimeout(timer);
6995
+ }, [refreshSignal, sessionId, fetchTree]);
6924
6996
  useEffect(() => {
6925
6997
  if (!sessionId) {
6926
6998
  setFile(null);
@@ -7261,6 +7333,12 @@ function ConfirmDialog({
7261
7333
  }
7262
7334
 
7263
7335
  // src/runtime/events.ts
7336
+ var WORKSPACE_MUTATING_TOOLS = /* @__PURE__ */ new Set([
7337
+ "terminal",
7338
+ "write_file",
7339
+ "patch",
7340
+ "execute_code"
7341
+ ]);
7264
7342
  var AGENT_CHAT_LISTENED_EVENTS = [
7265
7343
  "user.message",
7266
7344
  "llm.request",
@@ -7309,7 +7387,8 @@ function createInitialAgentChatState(options = {}) {
7309
7387
  lastEventId: 0,
7310
7388
  sessionDone: false,
7311
7389
  hadDeltas: false,
7312
- terminal: false
7390
+ terminal: false,
7391
+ workspaceRefreshKey: 0
7313
7392
  };
7314
7393
  }
7315
7394
  function applyAgentChatEvent(state, event) {
@@ -7371,11 +7450,17 @@ function applyAgentChatEvent(state, event) {
7371
7450
  return applyLlmThinking(nextState, event);
7372
7451
  case "tool.call":
7373
7452
  return applyToolCall(nextState, event);
7374
- case "tool.result":
7375
- return withMessages(
7376
- nextState,
7377
- applyToolResult(nextState.messages, event.data)
7378
- );
7453
+ case "tool.result": {
7454
+ const toolCallId = stringValue(event.data.tool_call_id);
7455
+ const toolName = findToolNameById(nextState.messages, toolCallId);
7456
+ const messages = applyToolResult(nextState.messages, event.data);
7457
+ const mutatesWorkspace = toolName !== null && WORKSPACE_MUTATING_TOOLS.has(toolName);
7458
+ return {
7459
+ ...nextState,
7460
+ messages,
7461
+ workspaceRefreshKey: mutatesWorkspace ? nextState.workspaceRefreshKey + 1 : nextState.workspaceRefreshKey
7462
+ };
7463
+ }
7379
7464
  case "harness.wake":
7380
7465
  case "llm.request":
7381
7466
  return nextState.terminal ? nextState : { ...nextState, isRunning: true };
@@ -7815,6 +7900,14 @@ function hasUserAfterIndex(messages, idx) {
7815
7900
  }
7816
7901
  return false;
7817
7902
  }
7903
+ function findToolNameById(messages, toolCallId) {
7904
+ if (!toolCallId) return null;
7905
+ for (let i = messages.length - 1; i >= 0; i--) {
7906
+ const tc = messages[i]?.toolCalls?.find((c) => c.id === toolCallId);
7907
+ if (tc) return tc.toolName;
7908
+ }
7909
+ return null;
7910
+ }
7818
7911
  function findLatestConsultExpertCall(messages) {
7819
7912
  for (let i = messages.length - 1; i >= 0; i--) {
7820
7913
  const msg = messages[i];
@@ -7946,23 +8039,27 @@ function useAgentChatRuntime({
7946
8039
  closeStream();
7947
8040
  };
7948
8041
  }, [adapter, clearReconnectTimer, closeStream, sessionId]);
7949
- const markSending = useCallback((content) => {
7950
- setState((prev) => ({
7951
- ...prev,
7952
- terminal: false,
7953
- isRunning: true,
7954
- messages: [
7955
- ...prev.messages,
7956
- {
7957
- id: `local-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
7958
- role: "user",
7959
- content,
7960
- createdAt: /* @__PURE__ */ new Date(),
7961
- status: "complete"
7962
- }
7963
- ]
7964
- }));
7965
- }, []);
8042
+ const markSending = useCallback(
8043
+ (content, images) => {
8044
+ setState((prev) => ({
8045
+ ...prev,
8046
+ terminal: false,
8047
+ isRunning: true,
8048
+ messages: [
8049
+ ...prev.messages,
8050
+ {
8051
+ id: `local-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
8052
+ role: "user",
8053
+ content,
8054
+ createdAt: /* @__PURE__ */ new Date(),
8055
+ status: "complete",
8056
+ images: images?.length ? images : void 0
8057
+ }
8058
+ ]
8059
+ }));
8060
+ },
8061
+ []
8062
+ );
7966
8063
  const markSendError = useCallback((errorText) => {
7967
8064
  setState((prev) => {
7968
8065
  for (let i = prev.messages.length - 1; i >= 0; i--) {
@@ -8013,13 +8110,13 @@ function useAgentChatRuntime({
8013
8110
  });
8014
8111
  }, []);
8015
8112
  const send = useCallback(
8016
- async (content) => {
8017
- markSending(content);
8113
+ async (content, images) => {
8114
+ markSending(content, images);
8018
8115
  if (!sessionId) {
8019
8116
  try {
8020
8117
  const session = await adapter.createSession({ agentId });
8021
8118
  onSessionChange?.(session.id);
8022
- await adapter.sendMessage({ sessionId: session.id, content });
8119
+ await adapter.sendMessage({ sessionId: session.id, content, images });
8023
8120
  } catch (error) {
8024
8121
  markSendError(error instanceof Error ? error.message : "send failed");
8025
8122
  throw error;
@@ -8027,7 +8124,7 @@ function useAgentChatRuntime({
8027
8124
  return;
8028
8125
  }
8029
8126
  try {
8030
- await adapter.sendMessage({ sessionId, content });
8127
+ await adapter.sendMessage({ sessionId, content, images });
8031
8128
  } catch (error) {
8032
8129
  markSendError(error instanceof Error ? error.message : "send failed");
8033
8130
  throw error;
@@ -8065,6 +8162,7 @@ function useAgentChatRuntime({
8065
8162
  isLoadingHistory: state.isLoadingHistory,
8066
8163
  tokenUsage: state.tokenUsage,
8067
8164
  retryIndicator: state.retryIndicator,
8165
+ workspaceRefreshKey: state.workspaceRefreshKey,
8068
8166
  send,
8069
8167
  stop,
8070
8168
  retry,
@@ -8132,7 +8230,7 @@ function AgentChat({
8132
8230
  messages: runtime.messages,
8133
8231
  isRunning: runtime.isRunning,
8134
8232
  isLoadingHistory: runtime.isLoadingHistory,
8135
- onSend: (content) => void runtime.send(content),
8233
+ onSend: (content, images) => void runtime.send(content, images),
8136
8234
  onStop: () => void runtime.stop(),
8137
8235
  onRetry: runtime.retry,
8138
8236
  onFileSelect: handleFileSelect,
@@ -8150,6 +8248,7 @@ function AgentChat({
8150
8248
  onSelectedPathChange: setWorkspacePath,
8151
8249
  collapsed: workspaceCollapsed,
8152
8250
  onCollapsedChange: setWorkspaceCollapsed,
8251
+ refreshSignal: runtime.workspaceRefreshKey,
8153
8252
  disabled
8154
8253
  }
8155
8254
  )
@@ -8254,6 +8353,7 @@ function TreeNodeRow({
8254
8353
  const hasChildren = entry.children.length > 0;
8255
8354
  const isActive = entry.id === activeSessionId;
8256
8355
  const isRunning = entry.status === "active";
8356
+ const isSubAgent = entry.parentId != null;
8257
8357
  const title = entry.title ?? "New session";
8258
8358
  const subtitle = [
8259
8359
  entry.model ?? "default",
@@ -8293,7 +8393,7 @@ function TreeNodeRow({
8293
8393
  /* @__PURE__ */ jsx("div", { className: "text-sm truncate", children: title }),
8294
8394
  /* @__PURE__ */ jsx("div", { className: "text-xs text-faint truncate", children: subtitle })
8295
8395
  ] }),
8296
- isRunning && canStop && /* @__PURE__ */ jsx(
8396
+ isRunning && canStop && isSubAgent && /* @__PURE__ */ jsx(
8297
8397
  "button",
8298
8398
  {
8299
8399
  type: "button",