@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.cjs CHANGED
@@ -19,9 +19,9 @@ var reactUseControllableState = require('@radix-ui/react-use-controllable-state'
19
19
  var shiki = require('shiki');
20
20
  var diff = require('diff');
21
21
  var Ansi = require('ansi-to-react');
22
- var tokenlens = require('tokenlens');
23
22
  var cmdk = require('cmdk');
24
23
  var nanoid = require('nanoid');
24
+ var tokenlens = require('tokenlens');
25
25
  require('pdfjs-dist/web/pdf_viewer.css');
26
26
  var dateFns = require('date-fns');
27
27
 
@@ -3555,7 +3555,7 @@ function ClarifyLocked({
3555
3555
  function ArtifactToolBlock({ tc }) {
3556
3556
  const args = parseArgs(tc.args) ?? {};
3557
3557
  const status = effectiveStatus(tc);
3558
- const label = status === "running" ? "Creating artifact\u2026" : status === "error" ? "Tried to create" : "Created";
3558
+ const label = status === "running" ? "Creating artifact\u2026" : status === "error" ? "Creating artifact\u2026" : "Created";
3559
3559
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 text-sm ", children: [
3560
3560
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-foreground", children: label }),
3561
3561
  args.name && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground truncate", children: args.name })
@@ -3947,7 +3947,18 @@ var ChatMessage = react.memo(function ChatMessage2({
3947
3947
  onFileSelect
3948
3948
  }) {
3949
3949
  if (message.role === "user") {
3950
- return /* @__PURE__ */ jsxRuntime.jsx(Message, { from: "user", children: /* @__PURE__ */ jsxRuntime.jsx(MessageContent, { children: message.content }) });
3950
+ return /* @__PURE__ */ jsxRuntime.jsx(Message, { from: "user", children: /* @__PURE__ */ jsxRuntime.jsxs(MessageContent, { children: [
3951
+ message.content,
3952
+ message.images && message.images.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2 mt-2", children: message.images.map((img, i) => /* @__PURE__ */ jsxRuntime.jsx(
3953
+ "img",
3954
+ {
3955
+ src: img.data,
3956
+ alt: "Attached",
3957
+ className: "max-w-64 max-h-48 rounded-lg border border-border object-contain"
3958
+ },
3959
+ i
3960
+ )) })
3961
+ ] }) });
3951
3962
  }
3952
3963
  return /* @__PURE__ */ jsxRuntime.jsx(AssistantMessage, { message, isLast, onFileSelect });
3953
3964
  });
@@ -3974,487 +3985,159 @@ function AssistantMessage({
3974
3985
  ] }) });
3975
3986
  }
3976
3987
 
3977
- // src/components/ui/hover-card.tsx
3988
+ // src/components/ui/command.tsx
3978
3989
  init_utils();
3979
- function HoverCard({
3990
+
3991
+ // src/components/ui/input-group.tsx
3992
+ init_utils();
3993
+ function InputGroup({ className, ...props }) {
3994
+ return /* @__PURE__ */ jsxRuntime.jsx(
3995
+ "div",
3996
+ {
3997
+ "data-slot": "input-group",
3998
+ role: "group",
3999
+ className: cn(
4000
+ "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",
4001
+ className
4002
+ ),
4003
+ ...props
4004
+ }
4005
+ );
4006
+ }
4007
+ var inputGroupAddonVariants = classVarianceAuthority.cva(
4008
+ "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",
4009
+ {
4010
+ variants: {
4011
+ align: {
4012
+ "inline-start": "order-first",
4013
+ "inline-end": "order-last",
4014
+ "block-start": "order-first w-full justify-start pt-3 group-has-[>input]/input-group:pt-3.5 [.border-b]:pb-3.5",
4015
+ "block-end": "order-last w-full justify-start pb-3 group-has-[>input]/input-group:pb-3.5 [.border-t]:pt-3.5"
4016
+ }
4017
+ },
4018
+ defaultVariants: {
4019
+ align: "inline-start"
4020
+ }
4021
+ }
4022
+ );
4023
+ function InputGroupAddon({
4024
+ className,
4025
+ align = "inline-start",
3980
4026
  ...props
3981
4027
  }) {
3982
- return /* @__PURE__ */ jsxRuntime.jsx(radixUi.HoverCard.Root, { "data-slot": "hover-card", ...props });
4028
+ return /* @__PURE__ */ jsxRuntime.jsx(
4029
+ "div",
4030
+ {
4031
+ role: "group",
4032
+ "data-slot": "input-group-addon",
4033
+ "data-align": align,
4034
+ className: cn(inputGroupAddonVariants({ align }), className),
4035
+ onClick: (e) => {
4036
+ if (e.target.closest("button")) {
4037
+ return;
4038
+ }
4039
+ e.currentTarget.parentElement?.querySelector("input")?.focus();
4040
+ },
4041
+ ...props
4042
+ }
4043
+ );
3983
4044
  }
3984
- function HoverCardTrigger({
4045
+ var inputGroupButtonVariants = classVarianceAuthority.cva(
4046
+ "flex items-center gap-2 rounded-none text-sm shadow-none",
4047
+ {
4048
+ variants: {
4049
+ size: {
4050
+ xs: "h-6 gap-1 rounded-none px-1.5 text-xs [&>svg:not([class*='size-'])]:size-3.5",
4051
+ sm: "",
4052
+ "icon-xs": "size-6 p-0 text-xs has-[>svg]:p-0",
4053
+ "icon-sm": "size-8 p-0 has-[>svg]:p-0"
4054
+ }
4055
+ },
4056
+ defaultVariants: {
4057
+ size: "xs"
4058
+ }
4059
+ }
4060
+ );
4061
+ function InputGroupButton({
4062
+ className,
4063
+ type = "button",
4064
+ variant = "ghost",
4065
+ size = "xs",
3985
4066
  ...props
3986
4067
  }) {
3987
- return /* @__PURE__ */ jsxRuntime.jsx(radixUi.HoverCard.Trigger, { "data-slot": "hover-card-trigger", ...props });
4068
+ return /* @__PURE__ */ jsxRuntime.jsx(
4069
+ Button,
4070
+ {
4071
+ type,
4072
+ "data-size": size,
4073
+ variant,
4074
+ className: cn(inputGroupButtonVariants({ size }), className),
4075
+ ...props
4076
+ }
4077
+ );
3988
4078
  }
3989
- function HoverCardContent({
4079
+ function InputGroupTextarea({
3990
4080
  className,
3991
- align = "center",
3992
- sideOffset = 4,
3993
4081
  ...props
3994
4082
  }) {
3995
- return /* @__PURE__ */ jsxRuntime.jsx(radixUi.HoverCard.Portal, { "data-slot": "hover-card-portal", children: /* @__PURE__ */ jsxRuntime.jsx(
3996
- radixUi.HoverCard.Content,
4083
+ return /* @__PURE__ */ jsxRuntime.jsx(
4084
+ Textarea,
3997
4085
  {
3998
- "data-slot": "hover-card-content",
3999
- align,
4000
- sideOffset,
4086
+ "data-slot": "input-group-control",
4001
4087
  className: cn(
4002
- "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",
4088
+ "flex-1 resize-none border-0 bg-transparent py-2.5 ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent",
4003
4089
  className
4004
4090
  ),
4005
4091
  ...props
4006
4092
  }
4007
- ) });
4093
+ );
4008
4094
  }
4009
-
4010
- // src/components/ui/progress.tsx
4011
- init_utils();
4012
- function Progress({
4095
+ function Command({
4013
4096
  className,
4014
- value,
4015
4097
  ...props
4016
4098
  }) {
4017
4099
  return /* @__PURE__ */ jsxRuntime.jsx(
4018
- radixUi.Progress.Root,
4100
+ cmdk.Command,
4019
4101
  {
4020
- "data-slot": "progress",
4102
+ "data-slot": "command",
4021
4103
  className: cn(
4022
- "relative flex h-0.5 w-full items-center overflow-x-hidden rounded-none bg-muted",
4104
+ "flex size-full flex-col overflow-hidden bg-popover text-popover-foreground",
4023
4105
  className
4024
4106
  ),
4025
- ...props,
4026
- children: /* @__PURE__ */ jsxRuntime.jsx(
4027
- radixUi.Progress.Indicator,
4028
- {
4029
- "data-slot": "progress-indicator",
4030
- className: "size-full flex-1 bg-primary transition-all",
4031
- style: { transform: `translateX(-${100 - (value || 0)}%)` }
4032
- }
4033
- )
4107
+ ...props
4034
4108
  }
4035
4109
  );
4036
4110
  }
4037
-
4038
- // src/components/ai-elements/context.tsx
4039
- init_utils();
4040
- var PERCENT_MAX = 100;
4041
- var ICON_RADIUS = 10;
4042
- var ICON_VIEWBOX = 24;
4043
- var ICON_CENTER = 12;
4044
- var ICON_STROKE_WIDTH = 2;
4045
- var ContextContext = react.createContext(null);
4046
- var useContextValue = () => {
4047
- const context = react.useContext(ContextContext);
4048
- if (!context) {
4049
- throw new Error("Context components must be used within Context");
4050
- }
4051
- return context;
4052
- };
4053
- var Context = ({
4054
- usedTokens,
4055
- maxTokens,
4056
- usage,
4057
- modelId,
4111
+ function CommandList({
4112
+ className,
4058
4113
  ...props
4059
- }) => {
4060
- const contextValue = react.useMemo(
4061
- () => ({ maxTokens, modelId, usage, usedTokens }),
4062
- [maxTokens, modelId, usage, usedTokens]
4063
- );
4064
- return /* @__PURE__ */ jsxRuntime.jsx(ContextContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(HoverCard, { closeDelay: 0, openDelay: 0, ...props }) });
4065
- };
4066
- var ContextIcon = () => {
4067
- const { usedTokens, maxTokens } = useContextValue();
4068
- const circumference = 2 * Math.PI * ICON_RADIUS;
4069
- const usedPercent = usedTokens / maxTokens;
4070
- const dashOffset = circumference * (1 - usedPercent);
4071
- return /* @__PURE__ */ jsxRuntime.jsxs(
4072
- "svg",
4114
+ }) {
4115
+ return /* @__PURE__ */ jsxRuntime.jsx(
4116
+ cmdk.Command.List,
4073
4117
  {
4074
- "aria-label": "Model context usage",
4075
- height: "20",
4076
- role: "img",
4077
- style: { color: "currentcolor" },
4078
- viewBox: `0 0 ${ICON_VIEWBOX} ${ICON_VIEWBOX}`,
4079
- width: "20",
4080
- children: [
4081
- /* @__PURE__ */ jsxRuntime.jsx(
4082
- "circle",
4083
- {
4084
- cx: ICON_CENTER,
4085
- cy: ICON_CENTER,
4086
- fill: "none",
4087
- opacity: "0.25",
4088
- r: ICON_RADIUS,
4089
- stroke: "currentColor",
4090
- strokeWidth: ICON_STROKE_WIDTH
4091
- }
4092
- ),
4093
- /* @__PURE__ */ jsxRuntime.jsx(
4094
- "circle",
4095
- {
4096
- cx: ICON_CENTER,
4097
- cy: ICON_CENTER,
4098
- fill: "none",
4099
- opacity: "0.7",
4100
- r: ICON_RADIUS,
4101
- stroke: "currentColor",
4102
- strokeDasharray: `${circumference} ${circumference}`,
4103
- strokeDashoffset: dashOffset,
4104
- strokeLinecap: "round",
4105
- strokeWidth: ICON_STROKE_WIDTH,
4106
- style: { transform: "rotate(-90deg)", transformOrigin: "center" }
4107
- }
4108
- )
4109
- ]
4118
+ "data-slot": "command-list",
4119
+ className: cn(
4120
+ "no-scrollbar max-h-72 scroll-py-1 overflow-x-hidden overflow-y-auto outline-none",
4121
+ className
4122
+ ),
4123
+ ...props
4110
4124
  }
4111
4125
  );
4112
- };
4113
- var ContextTrigger = ({ children, ...props }) => {
4114
- return /* @__PURE__ */ jsxRuntime.jsx(HoverCardTrigger, { asChild: true, children: children ?? /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", variant: "ghost", ...props, children: /* @__PURE__ */ jsxRuntime.jsx(ContextIcon, {}) }) });
4115
- };
4116
- var ContextContent = ({
4126
+ }
4127
+ function CommandEmpty({
4117
4128
  className,
4118
4129
  ...props
4119
- }) => /* @__PURE__ */ jsxRuntime.jsx(
4120
- HoverCardContent,
4121
- {
4122
- className: cn("min-w-60 divide-y overflow-hidden p-0", className),
4123
- ...props
4124
- }
4125
- );
4126
- var ContextContentHeader = ({
4127
- children,
4128
- className,
4129
- ...props
4130
- }) => {
4131
- const { usedTokens, maxTokens } = useContextValue();
4132
- const usedPercent = usedTokens / maxTokens;
4133
- const displayPct = new Intl.NumberFormat("en-US", {
4134
- maximumFractionDigits: 1,
4135
- style: "percent"
4136
- }).format(usedPercent);
4137
- const used = new Intl.NumberFormat("en-US", {
4138
- notation: "compact"
4139
- }).format(usedTokens);
4140
- const total = new Intl.NumberFormat("en-US", {
4141
- notation: "compact"
4142
- }).format(maxTokens);
4143
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("w-full space-y-2 p-3", className), ...props, children: children ?? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4144
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 text-xs", children: [
4145
- /* @__PURE__ */ jsxRuntime.jsx("p", { children: displayPct }),
4146
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: " text-muted-foreground", children: [
4147
- used,
4148
- " / ",
4149
- total
4150
- ] })
4151
- ] }),
4152
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: /* @__PURE__ */ jsxRuntime.jsx(Progress, { className: "bg-muted", value: usedPercent * PERCENT_MAX }) })
4153
- ] }) });
4154
- };
4155
- var ContextContentBody = ({
4156
- children,
4157
- className,
4158
- ...props
4159
- }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("w-full p-3", className), ...props, children });
4160
- var TokensWithCost = ({
4161
- tokens,
4162
- costText
4163
- }) => /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
4164
- tokens === void 0 ? "\u2014" : new Intl.NumberFormat("en-US", {
4165
- notation: "compact"
4166
- }).format(tokens),
4167
- costText ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-2 text-muted-foreground", children: [
4168
- "\u2022 ",
4169
- costText
4170
- ] }) : null
4171
- ] });
4172
- var ContextInputUsage = ({
4173
- className,
4174
- children,
4175
- ...props
4176
- }) => {
4177
- const { usage, modelId } = useContextValue();
4178
- const inputTokens = usage?.inputTokens ?? 0;
4179
- if (children) {
4180
- return children;
4181
- }
4182
- if (!inputTokens) {
4183
- return null;
4184
- }
4185
- const inputCost = modelId ? tokenlens.getUsage({
4186
- modelId,
4187
- usage: { input: inputTokens, output: 0 }
4188
- }).costUSD?.totalUSD : void 0;
4189
- const inputCostText = new Intl.NumberFormat("en-US", {
4190
- currency: "USD",
4191
- style: "currency"
4192
- }).format(inputCost ?? 0);
4193
- return /* @__PURE__ */ jsxRuntime.jsxs(
4194
- "div",
4195
- {
4196
- className: cn("flex items-center justify-between text-xs", className),
4197
- ...props,
4198
- children: [
4199
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Input" }),
4200
- /* @__PURE__ */ jsxRuntime.jsx(TokensWithCost, { costText: inputCostText, tokens: inputTokens })
4201
- ]
4202
- }
4203
- );
4204
- };
4205
- var ContextOutputUsage = ({
4206
- className,
4207
- children,
4208
- ...props
4209
- }) => {
4210
- const { usage, modelId } = useContextValue();
4211
- const outputTokens = usage?.outputTokens ?? 0;
4212
- if (children) {
4213
- return children;
4214
- }
4215
- if (!outputTokens) {
4216
- return null;
4217
- }
4218
- const outputCost = modelId ? tokenlens.getUsage({
4219
- modelId,
4220
- usage: { input: 0, output: outputTokens }
4221
- }).costUSD?.totalUSD : void 0;
4222
- const outputCostText = new Intl.NumberFormat("en-US", {
4223
- currency: "USD",
4224
- style: "currency"
4225
- }).format(outputCost ?? 0);
4226
- return /* @__PURE__ */ jsxRuntime.jsxs(
4227
- "div",
4228
- {
4229
- className: cn("flex items-center justify-between text-xs", className),
4230
- ...props,
4231
- children: [
4232
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Output" }),
4233
- /* @__PURE__ */ jsxRuntime.jsx(TokensWithCost, { costText: outputCostText, tokens: outputTokens })
4234
- ]
4235
- }
4236
- );
4237
- };
4238
- var ContextReasoningUsage = ({
4239
- className,
4240
- children,
4241
- ...props
4242
- }) => {
4243
- const { usage, modelId } = useContextValue();
4244
- const reasoningTokens = usage?.reasoningTokens ?? 0;
4245
- if (children) {
4246
- return children;
4247
- }
4248
- if (!reasoningTokens) {
4249
- return null;
4250
- }
4251
- const reasoningCost = modelId ? tokenlens.getUsage({
4252
- modelId,
4253
- usage: { reasoningTokens }
4254
- }).costUSD?.totalUSD : void 0;
4255
- const reasoningCostText = new Intl.NumberFormat("en-US", {
4256
- currency: "USD",
4257
- style: "currency"
4258
- }).format(reasoningCost ?? 0);
4259
- return /* @__PURE__ */ jsxRuntime.jsxs(
4260
- "div",
4261
- {
4262
- className: cn("flex items-center justify-between text-xs", className),
4263
- ...props,
4264
- children: [
4265
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Reasoning" }),
4266
- /* @__PURE__ */ jsxRuntime.jsx(TokensWithCost, { costText: reasoningCostText, tokens: reasoningTokens })
4267
- ]
4268
- }
4269
- );
4270
- };
4271
- var ContextCacheUsage = ({
4272
- className,
4273
- children,
4274
- ...props
4275
- }) => {
4276
- const { usage, modelId } = useContextValue();
4277
- const cacheTokens = usage?.cachedInputTokens ?? 0;
4278
- if (children) {
4279
- return children;
4280
- }
4281
- if (!cacheTokens) {
4282
- return null;
4283
- }
4284
- const cacheCost = modelId ? tokenlens.getUsage({
4285
- modelId,
4286
- usage: { cacheReads: cacheTokens, input: 0, output: 0 }
4287
- }).costUSD?.totalUSD : void 0;
4288
- const cacheCostText = new Intl.NumberFormat("en-US", {
4289
- currency: "USD",
4290
- style: "currency"
4291
- }).format(cacheCost ?? 0);
4292
- return /* @__PURE__ */ jsxRuntime.jsxs(
4293
- "div",
4294
- {
4295
- className: cn("flex items-center justify-between text-xs", className),
4296
- ...props,
4297
- children: [
4298
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Cache" }),
4299
- /* @__PURE__ */ jsxRuntime.jsx(TokensWithCost, { costText: cacheCostText, tokens: cacheTokens })
4300
- ]
4301
- }
4302
- );
4303
- };
4304
-
4305
- // src/components/ui/command.tsx
4306
- init_utils();
4307
-
4308
- // src/components/ui/input-group.tsx
4309
- init_utils();
4310
- function InputGroup({ className, ...props }) {
4311
- return /* @__PURE__ */ jsxRuntime.jsx(
4312
- "div",
4313
- {
4314
- "data-slot": "input-group",
4315
- role: "group",
4316
- className: cn(
4317
- "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",
4318
- className
4319
- ),
4320
- ...props
4321
- }
4322
- );
4323
- }
4324
- var inputGroupAddonVariants = classVarianceAuthority.cva(
4325
- "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",
4326
- {
4327
- variants: {
4328
- align: {
4329
- "inline-start": "order-first",
4330
- "inline-end": "order-last",
4331
- "block-start": "order-first w-full justify-start pt-3 group-has-[>input]/input-group:pt-3.5 [.border-b]:pb-3.5",
4332
- "block-end": "order-last w-full justify-start pb-3 group-has-[>input]/input-group:pb-3.5 [.border-t]:pt-3.5"
4333
- }
4334
- },
4335
- defaultVariants: {
4336
- align: "inline-start"
4337
- }
4338
- }
4339
- );
4340
- function InputGroupAddon({
4341
- className,
4342
- align = "inline-start",
4343
- ...props
4344
- }) {
4345
- return /* @__PURE__ */ jsxRuntime.jsx(
4346
- "div",
4347
- {
4348
- role: "group",
4349
- "data-slot": "input-group-addon",
4350
- "data-align": align,
4351
- className: cn(inputGroupAddonVariants({ align }), className),
4352
- onClick: (e) => {
4353
- if (e.target.closest("button")) {
4354
- return;
4355
- }
4356
- e.currentTarget.parentElement?.querySelector("input")?.focus();
4357
- },
4358
- ...props
4359
- }
4360
- );
4361
- }
4362
- var inputGroupButtonVariants = classVarianceAuthority.cva(
4363
- "flex items-center gap-2 rounded-none text-sm shadow-none",
4364
- {
4365
- variants: {
4366
- size: {
4367
- xs: "h-6 gap-1 rounded-none px-1.5 text-xs [&>svg:not([class*='size-'])]:size-3.5",
4368
- sm: "",
4369
- "icon-xs": "size-6 p-0 text-xs has-[>svg]:p-0",
4370
- "icon-sm": "size-8 p-0 has-[>svg]:p-0"
4371
- }
4372
- },
4373
- defaultVariants: {
4374
- size: "xs"
4375
- }
4376
- }
4377
- );
4378
- function InputGroupButton({
4379
- className,
4380
- type = "button",
4381
- variant = "ghost",
4382
- size = "xs",
4383
- ...props
4384
- }) {
4385
- return /* @__PURE__ */ jsxRuntime.jsx(
4386
- Button,
4387
- {
4388
- type,
4389
- "data-size": size,
4390
- variant,
4391
- className: cn(inputGroupButtonVariants({ size }), className),
4392
- ...props
4393
- }
4394
- );
4395
- }
4396
- function InputGroupTextarea({
4397
- className,
4398
- ...props
4399
- }) {
4400
- return /* @__PURE__ */ jsxRuntime.jsx(
4401
- Textarea,
4402
- {
4403
- "data-slot": "input-group-control",
4404
- className: cn(
4405
- "flex-1 resize-none border-0 bg-transparent py-2.5 ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent",
4406
- className
4407
- ),
4408
- ...props
4409
- }
4410
- );
4411
- }
4412
- function Command({
4413
- className,
4414
- ...props
4415
- }) {
4416
- return /* @__PURE__ */ jsxRuntime.jsx(
4417
- cmdk.Command,
4418
- {
4419
- "data-slot": "command",
4420
- className: cn(
4421
- "flex size-full flex-col overflow-hidden bg-popover text-popover-foreground",
4422
- className
4423
- ),
4424
- ...props
4425
- }
4426
- );
4427
- }
4428
- function CommandList({
4429
- className,
4430
- ...props
4431
- }) {
4432
- return /* @__PURE__ */ jsxRuntime.jsx(
4433
- cmdk.Command.List,
4434
- {
4435
- "data-slot": "command-list",
4436
- className: cn(
4437
- "no-scrollbar max-h-72 scroll-py-1 overflow-x-hidden overflow-y-auto outline-none",
4438
- className
4439
- ),
4440
- ...props
4441
- }
4442
- );
4443
- }
4444
- function CommandEmpty({
4445
- className,
4446
- ...props
4447
- }) {
4448
- return /* @__PURE__ */ jsxRuntime.jsx(
4449
- cmdk.Command.Empty,
4450
- {
4451
- "data-slot": "command-empty",
4452
- className: cn("py-6 text-center text-sm", className),
4453
- ...props
4454
- }
4455
- );
4456
- }
4457
- function CommandGroup({
4130
+ }) {
4131
+ return /* @__PURE__ */ jsxRuntime.jsx(
4132
+ cmdk.Command.Empty,
4133
+ {
4134
+ "data-slot": "command-empty",
4135
+ className: cn("py-6 text-center text-sm", className),
4136
+ ...props
4137
+ }
4138
+ );
4139
+ }
4140
+ function CommandGroup({
4458
4141
  className,
4459
4142
  ...props
4460
4143
  }) {
@@ -4548,6 +4231,39 @@ function DropdownMenuItem({
4548
4231
  );
4549
4232
  }
4550
4233
 
4234
+ // src/components/ui/hover-card.tsx
4235
+ init_utils();
4236
+ function HoverCard({
4237
+ ...props
4238
+ }) {
4239
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.HoverCard.Root, { "data-slot": "hover-card", ...props });
4240
+ }
4241
+ function HoverCardTrigger({
4242
+ ...props
4243
+ }) {
4244
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.HoverCard.Trigger, { "data-slot": "hover-card-trigger", ...props });
4245
+ }
4246
+ function HoverCardContent({
4247
+ className,
4248
+ align = "center",
4249
+ sideOffset = 4,
4250
+ ...props
4251
+ }) {
4252
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.HoverCard.Portal, { "data-slot": "hover-card-portal", children: /* @__PURE__ */ jsxRuntime.jsx(
4253
+ radixUi.HoverCard.Content,
4254
+ {
4255
+ "data-slot": "hover-card-content",
4256
+ align,
4257
+ sideOffset,
4258
+ className: cn(
4259
+ "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",
4260
+ className
4261
+ ),
4262
+ ...props
4263
+ }
4264
+ ) });
4265
+ }
4266
+
4551
4267
  // src/components/ui/spinner.tsx
4552
4268
  init_utils();
4553
4269
  function Spinner({ className, ...props }) {
@@ -4586,6 +4302,15 @@ var usePromptInputController = () => {
4586
4302
  return ctx;
4587
4303
  };
4588
4304
  var useOptionalPromptInputController = () => react.useContext(PromptInputController);
4305
+ var useProviderAttachments = () => {
4306
+ const ctx = react.useContext(ProviderAttachmentsContext);
4307
+ if (!ctx) {
4308
+ throw new Error(
4309
+ "Wrap your component inside <PromptInputProvider> to use useProviderAttachments()."
4310
+ );
4311
+ }
4312
+ return ctx;
4313
+ };
4589
4314
  var useOptionalProviderAttachments = () => react.useContext(ProviderAttachmentsContext);
4590
4315
  var PromptInputProvider = ({
4591
4316
  initialInput: initialTextInput = "",
@@ -5146,110 +4871,405 @@ var PromptInputTextarea = ({
5146
4871
  }
5147
4872
  );
5148
4873
  };
5149
- var PromptInputFooter = ({
4874
+ var PromptInputFooter = ({
4875
+ className,
4876
+ ...props
4877
+ }) => /* @__PURE__ */ jsxRuntime.jsx(
4878
+ InputGroupAddon,
4879
+ {
4880
+ align: "block-end",
4881
+ className: cn("justify-between gap-1", className),
4882
+ ...props
4883
+ }
4884
+ );
4885
+ var PromptInputTools = ({
4886
+ className,
4887
+ ...props
4888
+ }) => /* @__PURE__ */ jsxRuntime.jsx(
4889
+ "div",
4890
+ {
4891
+ className: cn("flex min-w-0 items-center gap-1", className),
4892
+ ...props
4893
+ }
4894
+ );
4895
+ var PromptInputButton = ({
4896
+ variant = "ghost",
4897
+ className,
4898
+ size,
4899
+ tooltip,
4900
+ ...props
4901
+ }) => {
4902
+ const newSize = size ?? (react.Children.count(props.children) > 1 ? "sm" : "icon-sm");
4903
+ const button = /* @__PURE__ */ jsxRuntime.jsx(
4904
+ InputGroupButton,
4905
+ {
4906
+ className: cn(className),
4907
+ size: newSize,
4908
+ type: "button",
4909
+ variant,
4910
+ ...props
4911
+ }
4912
+ );
4913
+ if (!tooltip) {
4914
+ return button;
4915
+ }
4916
+ const tooltipContent = typeof tooltip === "string" ? tooltip : tooltip.content;
4917
+ const shortcut = typeof tooltip === "string" ? void 0 : tooltip.shortcut;
4918
+ const side = typeof tooltip === "string" ? "top" : tooltip.side ?? "top";
4919
+ return /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
4920
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: button }),
4921
+ /* @__PURE__ */ jsxRuntime.jsxs(TooltipContent, { side, children: [
4922
+ tooltipContent,
4923
+ shortcut && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-2 text-muted-foreground", children: shortcut })
4924
+ ] })
4925
+ ] });
4926
+ };
4927
+ var PromptInputActionMenu = (props) => /* @__PURE__ */ jsxRuntime.jsx(DropdownMenu, { ...props });
4928
+ var PromptInputActionMenuTrigger = ({
4929
+ className,
4930
+ children,
4931
+ ...props
4932
+ }) => /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(PromptInputButton, { className, ...props, children: children ?? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PlusIcon, { className: "size-4" }) }) });
4933
+ var PromptInputActionMenuContent = ({
4934
+ className,
4935
+ ...props
4936
+ }) => /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuContent, { align: "start", className: cn(className), ...props });
4937
+ var PromptInputSubmit = ({
4938
+ className,
4939
+ variant = "default",
4940
+ size = "icon-sm",
4941
+ status,
4942
+ onStop,
4943
+ onClick,
4944
+ children,
4945
+ ...props
4946
+ }) => {
4947
+ const isGenerating = status === "submitted" || status === "streaming";
4948
+ let Icon = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CornerDownLeftIcon, { className: "size-4" });
4949
+ if (status === "submitted") {
4950
+ Icon = /* @__PURE__ */ jsxRuntime.jsx(Spinner, {});
4951
+ } else if (status === "streaming") {
4952
+ Icon = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SquareIcon, { className: "size-4" });
4953
+ } else if (status === "error") {
4954
+ Icon = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XIcon, { className: "size-4" });
4955
+ }
4956
+ const handleClick = react.useCallback(
4957
+ (e) => {
4958
+ if (isGenerating && onStop) {
4959
+ e.preventDefault();
4960
+ onStop();
4961
+ return;
4962
+ }
4963
+ onClick?.(e);
4964
+ },
4965
+ [isGenerating, onStop, onClick]
4966
+ );
4967
+ return /* @__PURE__ */ jsxRuntime.jsx(
4968
+ InputGroupButton,
4969
+ {
4970
+ "aria-label": isGenerating ? "Stop" : "Submit",
4971
+ className: cn(className),
4972
+ onClick: handleClick,
4973
+ size,
4974
+ type: isGenerating && onStop ? "button" : "submit",
4975
+ variant,
4976
+ ...props,
4977
+ children: children ?? Icon
4978
+ }
4979
+ );
4980
+ };
4981
+
4982
+ // src/components/ui/progress.tsx
4983
+ init_utils();
4984
+ function Progress({
4985
+ className,
4986
+ value,
4987
+ ...props
4988
+ }) {
4989
+ return /* @__PURE__ */ jsxRuntime.jsx(
4990
+ radixUi.Progress.Root,
4991
+ {
4992
+ "data-slot": "progress",
4993
+ className: cn(
4994
+ "relative flex h-0.5 w-full items-center overflow-x-hidden rounded-none bg-muted",
4995
+ className
4996
+ ),
4997
+ ...props,
4998
+ children: /* @__PURE__ */ jsxRuntime.jsx(
4999
+ radixUi.Progress.Indicator,
5000
+ {
5001
+ "data-slot": "progress-indicator",
5002
+ className: "size-full flex-1 bg-primary transition-all",
5003
+ style: { transform: `translateX(-${100 - (value || 0)}%)` }
5004
+ }
5005
+ )
5006
+ }
5007
+ );
5008
+ }
5009
+
5010
+ // src/components/ai-elements/context.tsx
5011
+ init_utils();
5012
+ var PERCENT_MAX = 100;
5013
+ var ICON_RADIUS = 10;
5014
+ var ICON_VIEWBOX = 24;
5015
+ var ICON_CENTER = 12;
5016
+ var ICON_STROKE_WIDTH = 2;
5017
+ var ContextContext = react.createContext(null);
5018
+ var useContextValue = () => {
5019
+ const context = react.useContext(ContextContext);
5020
+ if (!context) {
5021
+ throw new Error("Context components must be used within Context");
5022
+ }
5023
+ return context;
5024
+ };
5025
+ var Context = ({
5026
+ usedTokens,
5027
+ maxTokens,
5028
+ usage,
5029
+ modelId,
5030
+ ...props
5031
+ }) => {
5032
+ const contextValue = react.useMemo(
5033
+ () => ({ maxTokens, modelId, usage, usedTokens }),
5034
+ [maxTokens, modelId, usage, usedTokens]
5035
+ );
5036
+ return /* @__PURE__ */ jsxRuntime.jsx(ContextContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(HoverCard, { closeDelay: 0, openDelay: 0, ...props }) });
5037
+ };
5038
+ var ContextIcon = () => {
5039
+ const { usedTokens, maxTokens } = useContextValue();
5040
+ const circumference = 2 * Math.PI * ICON_RADIUS;
5041
+ const usedPercent = usedTokens / maxTokens;
5042
+ const dashOffset = circumference * (1 - usedPercent);
5043
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5044
+ "svg",
5045
+ {
5046
+ "aria-label": "Model context usage",
5047
+ height: "20",
5048
+ role: "img",
5049
+ style: { color: "currentcolor" },
5050
+ viewBox: `0 0 ${ICON_VIEWBOX} ${ICON_VIEWBOX}`,
5051
+ width: "20",
5052
+ children: [
5053
+ /* @__PURE__ */ jsxRuntime.jsx(
5054
+ "circle",
5055
+ {
5056
+ cx: ICON_CENTER,
5057
+ cy: ICON_CENTER,
5058
+ fill: "none",
5059
+ opacity: "0.25",
5060
+ r: ICON_RADIUS,
5061
+ stroke: "currentColor",
5062
+ strokeWidth: ICON_STROKE_WIDTH
5063
+ }
5064
+ ),
5065
+ /* @__PURE__ */ jsxRuntime.jsx(
5066
+ "circle",
5067
+ {
5068
+ cx: ICON_CENTER,
5069
+ cy: ICON_CENTER,
5070
+ fill: "none",
5071
+ opacity: "0.7",
5072
+ r: ICON_RADIUS,
5073
+ stroke: "currentColor",
5074
+ strokeDasharray: `${circumference} ${circumference}`,
5075
+ strokeDashoffset: dashOffset,
5076
+ strokeLinecap: "round",
5077
+ strokeWidth: ICON_STROKE_WIDTH,
5078
+ style: { transform: "rotate(-90deg)", transformOrigin: "center" }
5079
+ }
5080
+ )
5081
+ ]
5082
+ }
5083
+ );
5084
+ };
5085
+ var ContextTrigger = ({ children, ...props }) => {
5086
+ return /* @__PURE__ */ jsxRuntime.jsx(HoverCardTrigger, { asChild: true, children: children ?? /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", variant: "ghost", ...props, children: /* @__PURE__ */ jsxRuntime.jsx(ContextIcon, {}) }) });
5087
+ };
5088
+ var ContextContent = ({
5089
+ className,
5090
+ ...props
5091
+ }) => /* @__PURE__ */ jsxRuntime.jsx(
5092
+ HoverCardContent,
5093
+ {
5094
+ className: cn("min-w-60 divide-y overflow-hidden p-0", className),
5095
+ ...props
5096
+ }
5097
+ );
5098
+ var ContextContentHeader = ({
5099
+ children,
5100
+ className,
5101
+ ...props
5102
+ }) => {
5103
+ const { usedTokens, maxTokens } = useContextValue();
5104
+ const usedPercent = usedTokens / maxTokens;
5105
+ const displayPct = new Intl.NumberFormat("en-US", {
5106
+ maximumFractionDigits: 1,
5107
+ style: "percent"
5108
+ }).format(usedPercent);
5109
+ const used = new Intl.NumberFormat("en-US", {
5110
+ notation: "compact"
5111
+ }).format(usedTokens);
5112
+ const total = new Intl.NumberFormat("en-US", {
5113
+ notation: "compact"
5114
+ }).format(maxTokens);
5115
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("w-full space-y-2 p-3", className), ...props, children: children ?? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5116
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 text-xs", children: [
5117
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: displayPct }),
5118
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: " text-muted-foreground", children: [
5119
+ used,
5120
+ " / ",
5121
+ total
5122
+ ] })
5123
+ ] }),
5124
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: /* @__PURE__ */ jsxRuntime.jsx(Progress, { className: "bg-muted", value: usedPercent * PERCENT_MAX }) })
5125
+ ] }) });
5126
+ };
5127
+ var ContextContentBody = ({
5128
+ children,
5150
5129
  className,
5151
5130
  ...props
5152
- }) => /* @__PURE__ */ jsxRuntime.jsx(
5153
- InputGroupAddon,
5154
- {
5155
- align: "block-end",
5156
- className: cn("justify-between gap-1", className),
5157
- ...props
5158
- }
5159
- );
5160
- var PromptInputTools = ({
5131
+ }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("w-full p-3", className), ...props, children });
5132
+ var TokensWithCost = ({
5133
+ tokens,
5134
+ costText
5135
+ }) => /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
5136
+ tokens === void 0 ? "\u2014" : new Intl.NumberFormat("en-US", {
5137
+ notation: "compact"
5138
+ }).format(tokens),
5139
+ costText ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-2 text-muted-foreground", children: [
5140
+ "\u2022 ",
5141
+ costText
5142
+ ] }) : null
5143
+ ] });
5144
+ var ContextInputUsage = ({
5161
5145
  className,
5146
+ children,
5162
5147
  ...props
5163
- }) => /* @__PURE__ */ jsxRuntime.jsx(
5164
- "div",
5165
- {
5166
- className: cn("flex min-w-0 items-center gap-1", className),
5167
- ...props
5148
+ }) => {
5149
+ const { usage, modelId } = useContextValue();
5150
+ const inputTokens = usage?.inputTokens ?? 0;
5151
+ if (children) {
5152
+ return children;
5168
5153
  }
5169
- );
5170
- var PromptInputButton = ({
5171
- variant = "ghost",
5154
+ if (!inputTokens) {
5155
+ return null;
5156
+ }
5157
+ const inputCost = modelId ? tokenlens.getUsage({
5158
+ modelId,
5159
+ usage: { input: inputTokens, output: 0 }
5160
+ }).costUSD?.totalUSD : void 0;
5161
+ const inputCostText = new Intl.NumberFormat("en-US", {
5162
+ currency: "USD",
5163
+ style: "currency"
5164
+ }).format(inputCost ?? 0);
5165
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5166
+ "div",
5167
+ {
5168
+ className: cn("flex items-center justify-between text-xs", className),
5169
+ ...props,
5170
+ children: [
5171
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Input" }),
5172
+ /* @__PURE__ */ jsxRuntime.jsx(TokensWithCost, { costText: inputCostText, tokens: inputTokens })
5173
+ ]
5174
+ }
5175
+ );
5176
+ };
5177
+ var ContextOutputUsage = ({
5172
5178
  className,
5173
- size,
5174
- tooltip,
5179
+ children,
5175
5180
  ...props
5176
5181
  }) => {
5177
- const newSize = size ?? (react.Children.count(props.children) > 1 ? "sm" : "icon-sm");
5178
- const button = /* @__PURE__ */ jsxRuntime.jsx(
5179
- InputGroupButton,
5182
+ const { usage, modelId } = useContextValue();
5183
+ const outputTokens = usage?.outputTokens ?? 0;
5184
+ if (children) {
5185
+ return children;
5186
+ }
5187
+ if (!outputTokens) {
5188
+ return null;
5189
+ }
5190
+ const outputCost = modelId ? tokenlens.getUsage({
5191
+ modelId,
5192
+ usage: { input: 0, output: outputTokens }
5193
+ }).costUSD?.totalUSD : void 0;
5194
+ const outputCostText = new Intl.NumberFormat("en-US", {
5195
+ currency: "USD",
5196
+ style: "currency"
5197
+ }).format(outputCost ?? 0);
5198
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5199
+ "div",
5180
5200
  {
5181
- className: cn(className),
5182
- size: newSize,
5183
- type: "button",
5184
- variant,
5185
- ...props
5201
+ className: cn("flex items-center justify-between text-xs", className),
5202
+ ...props,
5203
+ children: [
5204
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Output" }),
5205
+ /* @__PURE__ */ jsxRuntime.jsx(TokensWithCost, { costText: outputCostText, tokens: outputTokens })
5206
+ ]
5186
5207
  }
5187
5208
  );
5188
- if (!tooltip) {
5189
- return button;
5190
- }
5191
- const tooltipContent = typeof tooltip === "string" ? tooltip : tooltip.content;
5192
- const shortcut = typeof tooltip === "string" ? void 0 : tooltip.shortcut;
5193
- const side = typeof tooltip === "string" ? "top" : tooltip.side ?? "top";
5194
- return /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
5195
- /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: button }),
5196
- /* @__PURE__ */ jsxRuntime.jsxs(TooltipContent, { side, children: [
5197
- tooltipContent,
5198
- shortcut && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-2 text-muted-foreground", children: shortcut })
5199
- ] })
5200
- ] });
5201
5209
  };
5202
- var PromptInputActionMenu = (props) => /* @__PURE__ */ jsxRuntime.jsx(DropdownMenu, { ...props });
5203
- var PromptInputActionMenuTrigger = ({
5210
+ var ContextReasoningUsage = ({
5204
5211
  className,
5205
5212
  children,
5206
5213
  ...props
5207
- }) => /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(PromptInputButton, { className, ...props, children: children ?? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PlusIcon, { className: "size-4" }) }) });
5208
- var PromptInputActionMenuContent = ({
5209
- className,
5210
- ...props
5211
- }) => /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuContent, { align: "start", className: cn(className), ...props });
5212
- var PromptInputSubmit = ({
5214
+ }) => {
5215
+ const { usage, modelId } = useContextValue();
5216
+ const reasoningTokens = usage?.reasoningTokens ?? 0;
5217
+ if (children) {
5218
+ return children;
5219
+ }
5220
+ if (!reasoningTokens) {
5221
+ return null;
5222
+ }
5223
+ const reasoningCost = modelId ? tokenlens.getUsage({
5224
+ modelId,
5225
+ usage: { reasoningTokens }
5226
+ }).costUSD?.totalUSD : void 0;
5227
+ const reasoningCostText = new Intl.NumberFormat("en-US", {
5228
+ currency: "USD",
5229
+ style: "currency"
5230
+ }).format(reasoningCost ?? 0);
5231
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5232
+ "div",
5233
+ {
5234
+ className: cn("flex items-center justify-between text-xs", className),
5235
+ ...props,
5236
+ children: [
5237
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Reasoning" }),
5238
+ /* @__PURE__ */ jsxRuntime.jsx(TokensWithCost, { costText: reasoningCostText, tokens: reasoningTokens })
5239
+ ]
5240
+ }
5241
+ );
5242
+ };
5243
+ var ContextCacheUsage = ({
5213
5244
  className,
5214
- variant = "default",
5215
- size = "icon-sm",
5216
- status,
5217
- onStop,
5218
- onClick,
5219
5245
  children,
5220
5246
  ...props
5221
5247
  }) => {
5222
- const isGenerating = status === "submitted" || status === "streaming";
5223
- let Icon = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CornerDownLeftIcon, { className: "size-4" });
5224
- if (status === "submitted") {
5225
- Icon = /* @__PURE__ */ jsxRuntime.jsx(Spinner, {});
5226
- } else if (status === "streaming") {
5227
- Icon = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SquareIcon, { className: "size-4" });
5228
- } else if (status === "error") {
5229
- Icon = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XIcon, { className: "size-4" });
5248
+ const { usage, modelId } = useContextValue();
5249
+ const cacheTokens = usage?.cachedInputTokens ?? 0;
5250
+ if (children) {
5251
+ return children;
5230
5252
  }
5231
- const handleClick = react.useCallback(
5232
- (e) => {
5233
- if (isGenerating && onStop) {
5234
- e.preventDefault();
5235
- onStop();
5236
- return;
5237
- }
5238
- onClick?.(e);
5239
- },
5240
- [isGenerating, onStop, onClick]
5241
- );
5242
- return /* @__PURE__ */ jsxRuntime.jsx(
5243
- InputGroupButton,
5253
+ if (!cacheTokens) {
5254
+ return null;
5255
+ }
5256
+ const cacheCost = modelId ? tokenlens.getUsage({
5257
+ modelId,
5258
+ usage: { cacheReads: cacheTokens, input: 0, output: 0 }
5259
+ }).costUSD?.totalUSD : void 0;
5260
+ const cacheCostText = new Intl.NumberFormat("en-US", {
5261
+ currency: "USD",
5262
+ style: "currency"
5263
+ }).format(cacheCost ?? 0);
5264
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5265
+ "div",
5244
5266
  {
5245
- "aria-label": isGenerating ? "Stop" : "Submit",
5246
- className: cn(className),
5247
- onClick: handleClick,
5248
- size,
5249
- type: isGenerating && onStop ? "button" : "submit",
5250
- variant,
5267
+ className: cn("flex items-center justify-between text-xs", className),
5251
5268
  ...props,
5252
- children: children ?? Icon
5269
+ children: [
5270
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Cache" }),
5271
+ /* @__PURE__ */ jsxRuntime.jsx(TokensWithCost, { costText: cacheCostText, tokens: cacheTokens })
5272
+ ]
5253
5273
  }
5254
5274
  );
5255
5275
  };
@@ -5289,6 +5309,29 @@ function PopoverAnchor({
5289
5309
  function ChatComposer(props) {
5290
5310
  return /* @__PURE__ */ jsxRuntime.jsx(PromptInputProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(ChatComposerInner, { ...props }) });
5291
5311
  }
5312
+ function AttachmentPreviewStrip() {
5313
+ const attachments = useProviderAttachments();
5314
+ if (attachments.files.length === 0) return null;
5315
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-2 px-3 pt-2 pb-1 flex-wrap", children: attachments.files.map((file) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative group", children: [
5316
+ file.mediaType?.startsWith("image/") && file.url ? /* @__PURE__ */ jsxRuntime.jsx(
5317
+ "img",
5318
+ {
5319
+ src: file.url,
5320
+ alt: file.filename,
5321
+ className: "h-16 w-16 rounded-lg border border-border object-cover"
5322
+ }
5323
+ ) : /* @__PURE__ */ jsxRuntime.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 }),
5324
+ /* @__PURE__ */ jsxRuntime.jsx(
5325
+ "button",
5326
+ {
5327
+ type: "button",
5328
+ onClick: () => attachments.remove(file.id),
5329
+ 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]",
5330
+ children: "\xD7"
5331
+ }
5332
+ )
5333
+ ] }, file.id)) });
5334
+ }
5292
5335
  function ChatComposerInner({
5293
5336
  onSend,
5294
5337
  onStop,
@@ -5381,7 +5424,13 @@ function ChatComposerInner({
5381
5424
  (message) => {
5382
5425
  const text = message.text.trim();
5383
5426
  if (!text || isRunning || disabled) return;
5384
- onSend(text);
5427
+ const images = [];
5428
+ for (const file of message.files) {
5429
+ if (file.mediaType?.startsWith("image/") && file.url) {
5430
+ images.push({ data: file.url, mimeType: file.mediaType });
5431
+ }
5432
+ }
5433
+ onSend(text, images.length > 0 ? images : void 0);
5385
5434
  },
5386
5435
  [onSend, isRunning, disabled]
5387
5436
  );
@@ -5390,7 +5439,8 @@ function ChatComposerInner({
5390
5439
  setButtonMenuOpen(false);
5391
5440
  }
5392
5441
  }, children: [
5393
- /* @__PURE__ */ jsxRuntime.jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(PromptInput, { onSubmit: handleSubmit, children: [
5442
+ /* @__PURE__ */ jsxRuntime.jsx(AttachmentPreviewStrip, {}),
5443
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(PromptInput, { onSubmit: handleSubmit, accept: "image/*", multiple: true, children: [
5394
5444
  /* @__PURE__ */ jsxRuntime.jsx(PromptInputBody, { children: /* @__PURE__ */ jsxRuntime.jsx(
5395
5445
  PromptInputTextarea,
5396
5446
  {
@@ -6234,14 +6284,17 @@ function TimelineEntryItem({
6234
6284
  ] });
6235
6285
  }
6236
6286
  if (entry.kind === "tool") {
6237
- const failureSummary = effectiveStatus(entry.tc) === "error" ? toolErrorSummary(entry.tc.result) : "";
6287
+ const rawStatus = effectiveStatus(entry.tc);
6288
+ const hideArtifactFailure = rawStatus === "error" && entry.tc.toolName === "create_artifact";
6289
+ const indicatorStatus = hideArtifactFailure ? "running" : rawStatus;
6290
+ const failureSummary = rawStatus === "error" && !hideArtifactFailure ? toolErrorSummary(entry.tc.result) : "";
6238
6291
  return /* @__PURE__ */ jsxRuntime.jsxs(TimelineItem, { step, children: [
6239
6292
  /* @__PURE__ */ jsxRuntime.jsxs(TimelineHeader, { children: [
6240
6293
  /* @__PURE__ */ jsxRuntime.jsx(TimelineSeparator, { style: { backgroundColor: "var(--color-border)" } }),
6241
6294
  /* @__PURE__ */ jsxRuntime.jsx(
6242
6295
  TimelineIndicator,
6243
6296
  {
6244
- className: cn("size-2 border-none", statusColorClass(effectiveStatus(entry.tc)))
6297
+ className: cn("size-2 border-none", statusColorClass(indicatorStatus))
6245
6298
  }
6246
6299
  )
6247
6300
  ] }),
@@ -6299,13 +6352,14 @@ function TimelineEntryItem({
6299
6352
  /* @__PURE__ */ jsxRuntime.jsx(TimelineSeparator, { style: { backgroundColor: "var(--color-border)" } }),
6300
6353
  /* @__PURE__ */ jsxRuntime.jsx(TimelineIndicator, { className: "size-2 border-none bg-primary animate-pulse" })
6301
6354
  ] }),
6302
- /* @__PURE__ */ jsxRuntime.jsx(TimelineContent, { children: /* @__PURE__ */ jsxRuntime.jsx(Shimmer, { duration: 5, className: "text-sm text-foreground", children: "Working on it..." }) })
6355
+ /* @__PURE__ */ jsxRuntime.jsx(TimelineContent, { children: /* @__PURE__ */ jsxRuntime.jsx(Shimmer, { duration: 3, spread: 3, className: "text-sm", children: "Working on it..." }) })
6303
6356
  ] });
6304
6357
  }
6305
6358
  function AssistantGroup({
6306
6359
  messages,
6307
6360
  lastGlobalIndex,
6308
6361
  totalMessages,
6362
+ isRunning,
6309
6363
  sessionId,
6310
6364
  onFileSelect,
6311
6365
  onRetry
@@ -6315,6 +6369,15 @@ function AssistantGroup({
6315
6369
  const isLast = i === messages.length - 1 && lastGlobalIndex === totalMessages - 1;
6316
6370
  entries.push(...messageToEntries(messages[i], isLast));
6317
6371
  }
6372
+ const isTailGroup = lastGlobalIndex === totalMessages - 1;
6373
+ const lastEntry = entries[entries.length - 1];
6374
+ const hasRunningTool = entries.some(
6375
+ (e) => e.kind === "tool" && e.tc.status === "running"
6376
+ );
6377
+ const tailMsg = messages[messages.length - 1];
6378
+ if (isTailGroup && isRunning && !hasRunningTool && lastEntry?.kind !== "thinking") {
6379
+ entries.push({ kind: "thinking", key: `${tailMsg.id}-tail-thinking` });
6380
+ }
6318
6381
  const tail = messages[messages.length - 1];
6319
6382
  const showErrorInfo = tail && tail.role === "assistant" && tail.status === "error" && !!tail.errorInfo;
6320
6383
  return /* @__PURE__ */ jsxRuntime.jsx(Message, { from: "assistant", children: /* @__PURE__ */ jsxRuntime.jsxs(MessageContent, { children: [
@@ -6438,6 +6501,7 @@ function ChatThread({
6438
6501
  messages: group.messages,
6439
6502
  lastGlobalIndex: group.lastGlobalIndex,
6440
6503
  totalMessages: messages.length,
6504
+ isRunning,
6441
6505
  sessionId,
6442
6506
  onFileSelect,
6443
6507
  onRetry: groupRetry
@@ -6445,7 +6509,7 @@ function ChatThread({
6445
6509
  group.messages[0].id
6446
6510
  );
6447
6511
  }),
6448
- isRunning && messages.length > 0 && messages[messages.length - 1].role === "user" && /* @__PURE__ */ jsxRuntime.jsx(Message, { from: "assistant", children: /* @__PURE__ */ jsxRuntime.jsx(MessageContent, { children: /* @__PURE__ */ jsxRuntime.jsx(Shimmer, { duration: 5, className: "text-sm", children: "Working on it..." }) }) })
6512
+ isRunning && messages.length > 0 && messages[messages.length - 1].role === "user" && /* @__PURE__ */ jsxRuntime.jsx(Message, { from: "assistant", children: /* @__PURE__ */ jsxRuntime.jsx(MessageContent, { children: /* @__PURE__ */ jsxRuntime.jsx(Shimmer, { duration: 3, spread: 3, className: "text-sm", children: "Working on it..." }) }) })
6449
6513
  ] }) }),
6450
6514
  /* @__PURE__ */ jsxRuntime.jsx(ConversationScrollButton, {})
6451
6515
  ] }),
@@ -7143,6 +7207,7 @@ function WorkspacePanel({
7143
7207
  onSelectedPathChange,
7144
7208
  collapsed = false,
7145
7209
  onCollapsedChange,
7210
+ refreshSignal = 0,
7146
7211
  disabled = false
7147
7212
  }) {
7148
7213
  const fileInputRef = react.useRef(null);
@@ -7222,6 +7287,13 @@ function WorkspacePanel({
7222
7287
  react.useEffect(() => {
7223
7288
  void fetchTree();
7224
7289
  }, [fetchTree]);
7290
+ react.useEffect(() => {
7291
+ if (!sessionId || refreshSignal === 0) return;
7292
+ const timer = setTimeout(() => {
7293
+ void fetchTree();
7294
+ }, 300);
7295
+ return () => clearTimeout(timer);
7296
+ }, [refreshSignal, sessionId, fetchTree]);
7225
7297
  react.useEffect(() => {
7226
7298
  if (!sessionId) {
7227
7299
  setFile(null);
@@ -7562,6 +7634,12 @@ function ConfirmDialog({
7562
7634
  }
7563
7635
 
7564
7636
  // src/runtime/events.ts
7637
+ var WORKSPACE_MUTATING_TOOLS = /* @__PURE__ */ new Set([
7638
+ "terminal",
7639
+ "write_file",
7640
+ "patch",
7641
+ "execute_code"
7642
+ ]);
7565
7643
  var AGENT_CHAT_LISTENED_EVENTS = [
7566
7644
  "user.message",
7567
7645
  "llm.request",
@@ -7610,7 +7688,8 @@ function createInitialAgentChatState(options = {}) {
7610
7688
  lastEventId: 0,
7611
7689
  sessionDone: false,
7612
7690
  hadDeltas: false,
7613
- terminal: false
7691
+ terminal: false,
7692
+ workspaceRefreshKey: 0
7614
7693
  };
7615
7694
  }
7616
7695
  function applyAgentChatEvent(state, event) {
@@ -7672,11 +7751,17 @@ function applyAgentChatEvent(state, event) {
7672
7751
  return applyLlmThinking(nextState, event);
7673
7752
  case "tool.call":
7674
7753
  return applyToolCall(nextState, event);
7675
- case "tool.result":
7676
- return withMessages(
7677
- nextState,
7678
- applyToolResult(nextState.messages, event.data)
7679
- );
7754
+ case "tool.result": {
7755
+ const toolCallId = stringValue(event.data.tool_call_id);
7756
+ const toolName = findToolNameById(nextState.messages, toolCallId);
7757
+ const messages = applyToolResult(nextState.messages, event.data);
7758
+ const mutatesWorkspace = toolName !== null && WORKSPACE_MUTATING_TOOLS.has(toolName);
7759
+ return {
7760
+ ...nextState,
7761
+ messages,
7762
+ workspaceRefreshKey: mutatesWorkspace ? nextState.workspaceRefreshKey + 1 : nextState.workspaceRefreshKey
7763
+ };
7764
+ }
7680
7765
  case "harness.wake":
7681
7766
  case "llm.request":
7682
7767
  return nextState.terminal ? nextState : { ...nextState, isRunning: true };
@@ -8116,6 +8201,14 @@ function hasUserAfterIndex(messages, idx) {
8116
8201
  }
8117
8202
  return false;
8118
8203
  }
8204
+ function findToolNameById(messages, toolCallId) {
8205
+ if (!toolCallId) return null;
8206
+ for (let i = messages.length - 1; i >= 0; i--) {
8207
+ const tc = messages[i]?.toolCalls?.find((c) => c.id === toolCallId);
8208
+ if (tc) return tc.toolName;
8209
+ }
8210
+ return null;
8211
+ }
8119
8212
  function findLatestConsultExpertCall(messages) {
8120
8213
  for (let i = messages.length - 1; i >= 0; i--) {
8121
8214
  const msg = messages[i];
@@ -8247,23 +8340,27 @@ function useAgentChatRuntime({
8247
8340
  closeStream();
8248
8341
  };
8249
8342
  }, [adapter, clearReconnectTimer, closeStream, sessionId]);
8250
- const markSending = react.useCallback((content) => {
8251
- setState((prev) => ({
8252
- ...prev,
8253
- terminal: false,
8254
- isRunning: true,
8255
- messages: [
8256
- ...prev.messages,
8257
- {
8258
- id: `local-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
8259
- role: "user",
8260
- content,
8261
- createdAt: /* @__PURE__ */ new Date(),
8262
- status: "complete"
8263
- }
8264
- ]
8265
- }));
8266
- }, []);
8343
+ const markSending = react.useCallback(
8344
+ (content, images) => {
8345
+ setState((prev) => ({
8346
+ ...prev,
8347
+ terminal: false,
8348
+ isRunning: true,
8349
+ messages: [
8350
+ ...prev.messages,
8351
+ {
8352
+ id: `local-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
8353
+ role: "user",
8354
+ content,
8355
+ createdAt: /* @__PURE__ */ new Date(),
8356
+ status: "complete",
8357
+ images: images?.length ? images : void 0
8358
+ }
8359
+ ]
8360
+ }));
8361
+ },
8362
+ []
8363
+ );
8267
8364
  const markSendError = react.useCallback((errorText) => {
8268
8365
  setState((prev) => {
8269
8366
  for (let i = prev.messages.length - 1; i >= 0; i--) {
@@ -8314,13 +8411,13 @@ function useAgentChatRuntime({
8314
8411
  });
8315
8412
  }, []);
8316
8413
  const send = react.useCallback(
8317
- async (content) => {
8318
- markSending(content);
8414
+ async (content, images) => {
8415
+ markSending(content, images);
8319
8416
  if (!sessionId) {
8320
8417
  try {
8321
8418
  const session = await adapter.createSession({ agentId });
8322
8419
  onSessionChange?.(session.id);
8323
- await adapter.sendMessage({ sessionId: session.id, content });
8420
+ await adapter.sendMessage({ sessionId: session.id, content, images });
8324
8421
  } catch (error) {
8325
8422
  markSendError(error instanceof Error ? error.message : "send failed");
8326
8423
  throw error;
@@ -8328,7 +8425,7 @@ function useAgentChatRuntime({
8328
8425
  return;
8329
8426
  }
8330
8427
  try {
8331
- await adapter.sendMessage({ sessionId, content });
8428
+ await adapter.sendMessage({ sessionId, content, images });
8332
8429
  } catch (error) {
8333
8430
  markSendError(error instanceof Error ? error.message : "send failed");
8334
8431
  throw error;
@@ -8366,6 +8463,7 @@ function useAgentChatRuntime({
8366
8463
  isLoadingHistory: state.isLoadingHistory,
8367
8464
  tokenUsage: state.tokenUsage,
8368
8465
  retryIndicator: state.retryIndicator,
8466
+ workspaceRefreshKey: state.workspaceRefreshKey,
8369
8467
  send,
8370
8468
  stop,
8371
8469
  retry,
@@ -8433,7 +8531,7 @@ function AgentChat({
8433
8531
  messages: runtime.messages,
8434
8532
  isRunning: runtime.isRunning,
8435
8533
  isLoadingHistory: runtime.isLoadingHistory,
8436
- onSend: (content) => void runtime.send(content),
8534
+ onSend: (content, images) => void runtime.send(content, images),
8437
8535
  onStop: () => void runtime.stop(),
8438
8536
  onRetry: runtime.retry,
8439
8537
  onFileSelect: handleFileSelect,
@@ -8451,6 +8549,7 @@ function AgentChat({
8451
8549
  onSelectedPathChange: setWorkspacePath,
8452
8550
  collapsed: workspaceCollapsed,
8453
8551
  onCollapsedChange: setWorkspaceCollapsed,
8552
+ refreshSignal: runtime.workspaceRefreshKey,
8454
8553
  disabled
8455
8554
  }
8456
8555
  )
@@ -8556,6 +8655,7 @@ function TreeNodeRow({
8556
8655
  const hasChildren = entry.children.length > 0;
8557
8656
  const isActive = entry.id === activeSessionId;
8558
8657
  const isRunning = entry.status === "active";
8658
+ const isSubAgent = entry.parentId != null;
8559
8659
  const title = entry.title ?? "New session";
8560
8660
  const subtitle = [
8561
8661
  entry.model ?? "default",
@@ -8595,7 +8695,7 @@ function TreeNodeRow({
8595
8695
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm truncate", children: title }),
8596
8696
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-faint truncate", children: subtitle })
8597
8697
  ] }),
8598
- isRunning && canStop && /* @__PURE__ */ jsxRuntime.jsx(
8698
+ isRunning && canStop && isSubAgent && /* @__PURE__ */ jsxRuntime.jsx(
8599
8699
  "button",
8600
8700
  {
8601
8701
  type: "button",