@townco/ui 0.1.114 → 0.1.116

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.
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ChevronDown, ListVideo } from "lucide-react";
3
+ import React, { useState } from "react";
4
+ import { ToolCall } from "./ToolCall.js";
5
+ /**
6
+ * ToolCallGroup component - displays a group of parallel tool calls with collapsible details
7
+ */
8
+ export function ToolCallGroup({ toolCalls }) {
9
+ const [isExpanded, setIsExpanded] = useState(false);
10
+ // Calculate group status based on individual tool call statuses
11
+ const getGroupStatus = () => {
12
+ const statuses = toolCalls.map((tc) => tc.status);
13
+ if (statuses.some((s) => s === "failed"))
14
+ return "failed";
15
+ if (statuses.some((s) => s === "in_progress"))
16
+ return "in_progress";
17
+ if (statuses.every((s) => s === "completed"))
18
+ return "completed";
19
+ return "pending";
20
+ };
21
+ const groupStatus = getGroupStatus();
22
+ // Generate summary of tool names
23
+ const toolNames = toolCalls.map((tc) => tc.prettyName || tc.title);
24
+ const uniqueNames = [...new Set(toolNames)];
25
+ const summary = uniqueNames.length <= 2
26
+ ? uniqueNames.join(", ")
27
+ : `${uniqueNames.slice(0, 2).join(", ")} +${uniqueNames.length - 2} more`;
28
+ return (_jsxs("div", { className: "flex flex-col my-4", children: [_jsxs("button", { type: "button", className: "flex flex-col items-start gap-0.5 cursor-pointer bg-transparent border-none p-0 text-left group w-fit", onClick: () => setIsExpanded(!isExpanded), "aria-expanded": isExpanded, children: [_jsxs("div", { className: "flex items-center gap-1.5 text-[11px] font-medium text-muted-foreground", children: [_jsx("div", { className: "text-muted-foreground", children: _jsx(ListVideo, { className: "h-3 w-3" }) }), _jsx("span", { className: "text-paragraph-sm text-muted-foreground", children: "Parallel operation" }), _jsx("span", { className: "text-[10px] bg-muted px-1.5 py-0.5 rounded text-muted-foreground/70", children: toolCalls.length }), _jsx(ChevronDown, { className: `h-3 w-3 text-muted-foreground/70 transition-transform duration-200 ${isExpanded ? "rotate-180" : ""}` })] }), !isExpanded && (_jsx("span", { className: "text-paragraph-sm text-muted-foreground/70 pl-4.5", children: summary }))] }), isExpanded && (_jsx("div", { className: "mt-1", children: toolCalls.map((toolCall) => (_jsxs("div", { className: "flex items-start", children: [_jsx("div", { className: "w-2.5 h-4 border-l-2 border-b-2 border-border rounded-bl-[6px] mt-1 mr-0.5 shrink-0" }), _jsx("div", { className: "flex-1 -mt-2", children: _jsx(ToolCall, { toolCall: toolCall }) })] }, toolCall.id))) }))] }));
29
+ }
@@ -5,6 +5,7 @@ import React, { useEffect, useRef, useState } from "react";
5
5
  import { getGroupDisplayState, getToolCallDisplayState, getToolCallStateVerbiage, isPreliminaryToolCall, } from "../../core/utils/tool-call-state.js";
6
6
  import { generateSmartSummary } from "../../core/utils/tool-summary.js";
7
7
  import * as ChatLayout from "./ChatLayout.js";
8
+ import { ContextUsageIndicator } from "./ContextUsageButton.js";
8
9
  import { SubAgentDetails } from "./SubAgentDetails.js";
9
10
  import { useTheme } from "./ThemeProvider.js";
10
11
  import { TodoSubline } from "./TodoSubline.js";
@@ -141,6 +142,20 @@ export function ToolOperation({ toolCalls, isGrouped = false, autoMinimize = tru
141
142
  // Detect subagent calls (subagents now run in-process, messages in subagentMessages)
142
143
  const isSubagentCall = !!(singleToolCall?.subagentMessages &&
143
144
  singleToolCall.subagentMessages.length > 0);
145
+ // Extract latest subagent context size (if provided by backend on subagent messages)
146
+ const subagentContextSize = React.useMemo(() => {
147
+ if (!singleToolCall?.subagentMessages)
148
+ return null;
149
+ const messages = singleToolCall.subagentMessages;
150
+ for (let i = messages.length - 1; i >= 0; i--) {
151
+ const msg = messages[i];
152
+ const cs = msg?._meta?.context_size ?? msg?.context_size;
153
+ if (cs != null)
154
+ return cs;
155
+ }
156
+ return null;
157
+ }, [singleToolCall?.subagentMessages]);
158
+ const subagentHeaderContextSize = subagentContextSize;
144
159
  // State for subagent expansion
145
160
  const [isSubagentExpanded, setIsSubagentExpanded] = useState(false);
146
161
  // Safely access ChatLayout context
@@ -253,7 +268,7 @@ export function ToolOperation({ toolCalls, isGrouped = false, autoMinimize = tru
253
268
  return (_jsxs("div", { className: "flex flex-col my-4", children: [
254
269
  _jsxs("button", { type: "button", className: "flex flex-col items-start gap-0.5 cursor-pointer bg-transparent border-none p-0 text-left group w-fit rounded-md px-1 -mx-1", onClick: handleHeaderClick, "aria-expanded": isTodoWrite ? undefined : isExpanded, children: [
255
270
  _jsxs("div", { className: "flex items-center gap-1.5", children: [
256
- _jsx("div", { className: "text-text-secondary/70 group-hover:text-text-secondary transition-colors", children: _jsx(IconComponent, { className: "h-3 w-3" }) }), _jsxs("span", { className: "text-paragraph-sm text-text-secondary/70 group-hover:text-text-secondary transition-colors", children: [isGrouped && _jsx("span", { className: "mr-1", children: "Parallel operation" }), !isGrouped && displayText] }), !isGrouped &&
271
+ _jsx("div", { className: "text-text-secondary/70 group-hover:text-text-secondary transition-colors", children: _jsx(IconComponent, { className: "h-3 w-3" }) }), _jsxs("span", { className: "text-paragraph-sm text-text-secondary/70 group-hover:text-text-secondary transition-colors", children: [isGrouped && _jsx("span", { className: "mr-1", children: "Parallel operation" }), !isGrouped && displayText] }), isSubagentCall && (_jsx(ContextUsageIndicator, { contextSize: subagentHeaderContextSize, size: 12, className: "text-text-secondary/70 group-hover:text-text-secondary transition-colors" })), !isGrouped &&
257
272
  singleToolCall?.startedAt &&
258
273
  displayState === "executing" && (_jsx(RunningDuration, { startTime: singleToolCall.startedAt, isRunning: !singleToolCall.subagentCompleted })), isGrouped &&
259
274
  displayState === "executing" &&
@@ -333,6 +348,18 @@ function GroupedToolCallItem({ toolCall, hookNotification, }) {
333
348
  const [isExpanded, setIsExpanded] = useState(false);
334
349
  // Detect subagent calls (subagents now run in-process, messages in subagentMessages)
335
350
  const isSubagentCall = !!(toolCall.subagentMessages && toolCall.subagentMessages.length > 0);
351
+ // Extract latest subagent context size (if provided by backend on subagent messages)
352
+ const subagentContextSize = React.useMemo(() => {
353
+ const messages = toolCall.subagentMessages ?? [];
354
+ for (let i = messages.length - 1; i >= 0; i--) {
355
+ const msg = messages[i];
356
+ const cs = msg?._meta?.context_size ?? msg?.context_size;
357
+ if (cs != null)
358
+ return cs;
359
+ }
360
+ return null;
361
+ }, [toolCall.subagentMessages]);
362
+ const subagentHeaderContextSize = subagentContextSize;
336
363
  // Detect compaction for this individual tool call
337
364
  const hasCompaction = !!((hookNotification?.status === "completed" &&
338
365
  hookNotification.metadata?.action &&
@@ -351,7 +378,7 @@ function GroupedToolCallItem({ toolCall, hookNotification, }) {
351
378
  _jsxs("button", { type: "button", className: "flex items-center gap-1.5 cursor-pointer bg-transparent border-none p-0 text-left group w-fit", onClick: () => setIsExpanded(!isExpanded), "aria-expanded": isExpanded, children: [
352
379
  _jsx("div", { className: "text-text-secondary/70 group-hover:text-text-secondary transition-colors", children: _jsx(CircleDot, { className: "h-3 w-3" }) }), _jsx("span", { className: "text-paragraph-sm text-text-secondary/70 group-hover:text-text-secondary transition-colors", children: toolCall.subagentMessages?.[0]?._meta?.semanticName ||
353
380
  toolCall.rawInput?.agentName ||
354
- "Subagent" }), toolCall.subagentMessages?.[0]?._meta?.semanticName && (_jsxs("span", { className: "text-muted-foreground text-[10px] ml-1", children: ["(", toolCall.rawInput?.agentName || "subagent", ")"] })), isRunning && toolCall.startedAt && (_jsx(RunningDuration, { startTime: toolCall.startedAt })), isFailed && (_jsx("span", { title: toolCall.error || "Operation failed", children: _jsx(AlertCircle, { className: "h-3 w-3 text-destructive" }) })), hasCompaction && (_jsx(TooltipProvider, { delayDuration: 0, children: _jsxs(Tooltip, { children: [
381
+ "Subagent" }), toolCall.subagentMessages?.[0]?._meta?.semanticName && (_jsxs("span", { className: "text-muted-foreground text-[10px] ml-1", children: ["(", toolCall.rawInput?.agentName || "subagent", ")"] })), _jsx(ContextUsageIndicator, { contextSize: subagentHeaderContextSize, size: 12, className: "text-text-secondary/70 group-hover:text-text-secondary transition-colors" }), isRunning && toolCall.startedAt && (_jsx(RunningDuration, { startTime: toolCall.startedAt })), isFailed && (_jsx("span", { title: toolCall.error || "Operation failed", children: _jsx(AlertCircle, { className: "h-3 w-3 text-destructive" }) })), hasCompaction && (_jsx(TooltipProvider, { delayDuration: 0, children: _jsxs(Tooltip, { children: [
355
382
  _jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { children: isTruncation ? (_jsx(ScissorsLineDashed, { className: "h-3 w-3 text-destructive" })) : (_jsx(FoldVertical, { className: "h-3 w-3 text-text-secondary/70" })) }) }), _jsx(TooltipContent, { children: (() => {
356
383
  const meta = toolCall._meta;
357
384
  const percentage = meta?.originalTokens && meta?.finalTokens
@@ -5,6 +5,8 @@ import { cn } from "../lib/utils.js";
5
5
  const TooltipProvider = TooltipPrimitive.Provider;
6
6
  const Tooltip = TooltipPrimitive.Root;
7
7
  const TooltipTrigger = TooltipPrimitive.Trigger;
8
- const TooltipContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => (_jsx(TooltipPrimitive.Content, { ref: ref, sideOffset: sideOffset, className: cn("z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 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", className), ...props })));
8
+ const TooltipContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => (_jsx(TooltipPrimitive.Portal, { children: _jsx(TooltipPrimitive.Content, { ref: ref, sideOffset: sideOffset, className: cn(
9
+ // High z-index so tooltips render above overlays/modals in the chat UI.
10
+ "z-[9999] overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 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", className), ...props }) })));
9
11
  TooltipContent.displayName = TooltipPrimitive.Content.displayName;
10
12
  export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
@@ -348,12 +348,36 @@ export declare const SessionUpdate: z.ZodUnion<readonly [z.ZodObject<{
348
348
  }, z.core.$strip>;
349
349
  }, z.core.$strip>], "type">>>;
350
350
  isStreaming: z.ZodOptional<z.ZodBoolean>;
351
+ context_size: z.ZodOptional<z.ZodObject<{
352
+ systemPromptTokens: z.ZodNumber;
353
+ toolOverheadTokens: z.ZodOptional<z.ZodNumber>;
354
+ mcpOverheadTokens: z.ZodOptional<z.ZodNumber>;
355
+ userMessagesTokens: z.ZodNumber;
356
+ assistantMessagesTokens: z.ZodNumber;
357
+ toolInputTokens: z.ZodNumber;
358
+ toolResultsTokens: z.ZodNumber;
359
+ totalEstimated: z.ZodNumber;
360
+ llmReportedInputTokens: z.ZodOptional<z.ZodNumber>;
361
+ modelContextWindow: z.ZodOptional<z.ZodNumber>;
362
+ }, z.core.$strip>>;
351
363
  _meta: z.ZodOptional<z.ZodObject<{
352
364
  semanticName: z.ZodOptional<z.ZodString>;
353
365
  agentDefinitionName: z.ZodOptional<z.ZodString>;
354
366
  currentActivity: z.ZodOptional<z.ZodString>;
355
367
  statusGenerating: z.ZodOptional<z.ZodBoolean>;
356
- }, z.core.$strip>>;
368
+ context_size: z.ZodOptional<z.ZodObject<{
369
+ systemPromptTokens: z.ZodNumber;
370
+ toolOverheadTokens: z.ZodOptional<z.ZodNumber>;
371
+ mcpOverheadTokens: z.ZodOptional<z.ZodNumber>;
372
+ userMessagesTokens: z.ZodNumber;
373
+ assistantMessagesTokens: z.ZodNumber;
374
+ toolInputTokens: z.ZodNumber;
375
+ toolResultsTokens: z.ZodNumber;
376
+ totalEstimated: z.ZodNumber;
377
+ llmReportedInputTokens: z.ZodOptional<z.ZodNumber>;
378
+ modelContextWindow: z.ZodOptional<z.ZodNumber>;
379
+ }, z.core.$strip>>;
380
+ }, z.core.$loose>>;
357
381
  }, z.core.$strip>>>;
358
382
  subagentStreaming: z.ZodOptional<z.ZodBoolean>;
359
383
  subagentCompleted: z.ZodOptional<z.ZodBoolean>;
@@ -563,12 +587,36 @@ export declare const SessionUpdate: z.ZodUnion<readonly [z.ZodObject<{
563
587
  }, z.core.$strip>;
564
588
  }, z.core.$strip>], "type">>>;
565
589
  isStreaming: z.ZodOptional<z.ZodBoolean>;
590
+ context_size: z.ZodOptional<z.ZodObject<{
591
+ systemPromptTokens: z.ZodNumber;
592
+ toolOverheadTokens: z.ZodOptional<z.ZodNumber>;
593
+ mcpOverheadTokens: z.ZodOptional<z.ZodNumber>;
594
+ userMessagesTokens: z.ZodNumber;
595
+ assistantMessagesTokens: z.ZodNumber;
596
+ toolInputTokens: z.ZodNumber;
597
+ toolResultsTokens: z.ZodNumber;
598
+ totalEstimated: z.ZodNumber;
599
+ llmReportedInputTokens: z.ZodOptional<z.ZodNumber>;
600
+ modelContextWindow: z.ZodOptional<z.ZodNumber>;
601
+ }, z.core.$strip>>;
566
602
  _meta: z.ZodOptional<z.ZodObject<{
567
603
  semanticName: z.ZodOptional<z.ZodString>;
568
604
  agentDefinitionName: z.ZodOptional<z.ZodString>;
569
605
  currentActivity: z.ZodOptional<z.ZodString>;
570
606
  statusGenerating: z.ZodOptional<z.ZodBoolean>;
571
- }, z.core.$strip>>;
607
+ context_size: z.ZodOptional<z.ZodObject<{
608
+ systemPromptTokens: z.ZodNumber;
609
+ toolOverheadTokens: z.ZodOptional<z.ZodNumber>;
610
+ mcpOverheadTokens: z.ZodOptional<z.ZodNumber>;
611
+ userMessagesTokens: z.ZodNumber;
612
+ assistantMessagesTokens: z.ZodNumber;
613
+ toolInputTokens: z.ZodNumber;
614
+ toolResultsTokens: z.ZodNumber;
615
+ totalEstimated: z.ZodNumber;
616
+ llmReportedInputTokens: z.ZodOptional<z.ZodNumber>;
617
+ modelContextWindow: z.ZodOptional<z.ZodNumber>;
618
+ }, z.core.$strip>>;
619
+ }, z.core.$loose>>;
572
620
  }, z.core.$strip>>>;
573
621
  subagentCompleted: z.ZodOptional<z.ZodBoolean>;
574
622
  _meta: z.ZodOptional<z.ZodObject<{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@townco/ui",
3
- "version": "0.1.114",
3
+ "version": "0.1.116",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -49,7 +49,7 @@
49
49
  "@radix-ui/react-slot": "^1.2.4",
50
50
  "@radix-ui/react-tabs": "^1.1.13",
51
51
  "@radix-ui/react-tooltip": "^1.2.8",
52
- "@townco/core": "0.0.92",
52
+ "@townco/core": "0.0.94",
53
53
  "@types/mdast": "^4.0.4",
54
54
  "@uiw/react-json-view": "^2.0.0-alpha.39",
55
55
  "class-variance-authority": "^0.7.1",
@@ -67,7 +67,7 @@
67
67
  "zustand": "^5.0.8"
68
68
  },
69
69
  "devDependencies": {
70
- "@townco/tsconfig": "0.1.111",
70
+ "@townco/tsconfig": "0.1.113",
71
71
  "@types/node": "^24.10.0",
72
72
  "@types/react": "^19.2.2",
73
73
  "@types/unist": "^3.0.3",