@townco/ui 0.1.67 → 0.1.69

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.
Files changed (56) hide show
  1. package/dist/core/hooks/use-chat-messages.d.ts +5 -0
  2. package/dist/core/hooks/use-tool-calls.d.ts +5 -0
  3. package/dist/core/hooks/use-tool-calls.js +2 -1
  4. package/dist/core/schemas/chat.d.ts +10 -0
  5. package/dist/core/schemas/tool-call.d.ts +96 -0
  6. package/dist/core/schemas/tool-call.js +12 -0
  7. package/dist/core/utils/tool-call-state.d.ts +30 -0
  8. package/dist/core/utils/tool-call-state.js +73 -0
  9. package/dist/core/utils/tool-summary.d.ts +13 -0
  10. package/dist/core/utils/tool-summary.js +172 -0
  11. package/dist/core/utils/tool-verbiage.d.ts +28 -0
  12. package/dist/core/utils/tool-verbiage.js +185 -0
  13. package/dist/gui/components/AppSidebar.d.ts +22 -0
  14. package/dist/gui/components/AppSidebar.js +22 -0
  15. package/dist/gui/components/ChatLayout.d.ts +5 -0
  16. package/dist/gui/components/ChatLayout.js +239 -132
  17. package/dist/gui/components/ChatView.js +42 -118
  18. package/dist/gui/components/MessageContent.js +199 -49
  19. package/dist/gui/components/SessionHistory.d.ts +10 -0
  20. package/dist/gui/components/SessionHistory.js +101 -0
  21. package/dist/gui/components/SessionHistoryItem.d.ts +11 -0
  22. package/dist/gui/components/SessionHistoryItem.js +24 -0
  23. package/dist/gui/components/Sheet.d.ts +25 -0
  24. package/dist/gui/components/Sheet.js +36 -0
  25. package/dist/gui/components/Sidebar.d.ts +65 -0
  26. package/dist/gui/components/Sidebar.js +231 -0
  27. package/dist/gui/components/SidebarToggle.d.ts +3 -0
  28. package/dist/gui/components/SidebarToggle.js +9 -0
  29. package/dist/gui/components/SubAgentDetails.d.ts +13 -6
  30. package/dist/gui/components/SubAgentDetails.js +29 -14
  31. package/dist/gui/components/ToolCallList.js +3 -3
  32. package/dist/gui/components/ToolOperation.d.ts +11 -0
  33. package/dist/gui/components/ToolOperation.js +289 -0
  34. package/dist/gui/components/WorkProgress.d.ts +20 -0
  35. package/dist/gui/components/WorkProgress.js +79 -0
  36. package/dist/gui/components/index.d.ts +8 -1
  37. package/dist/gui/components/index.js +9 -1
  38. package/dist/gui/hooks/index.d.ts +1 -0
  39. package/dist/gui/hooks/index.js +1 -0
  40. package/dist/gui/hooks/use-mobile.d.ts +1 -0
  41. package/dist/gui/hooks/use-mobile.js +15 -0
  42. package/dist/gui/index.d.ts +1 -0
  43. package/dist/gui/index.js +2 -0
  44. package/dist/gui/lib/motion.d.ts +55 -0
  45. package/dist/gui/lib/motion.js +217 -0
  46. package/dist/sdk/schemas/session.d.ts +102 -6
  47. package/dist/sdk/transports/http.js +105 -37
  48. package/dist/sdk/transports/types.d.ts +5 -0
  49. package/package.json +8 -7
  50. package/src/styles/global.css +128 -1
  51. package/dist/gui/components/InvokingGroup.d.ts +0 -9
  52. package/dist/gui/components/InvokingGroup.js +0 -16
  53. package/dist/gui/components/ToolCall.d.ts +0 -8
  54. package/dist/gui/components/ToolCall.js +0 -226
  55. package/dist/gui/components/ToolCallGroup.d.ts +0 -8
  56. package/dist/gui/components/ToolCallGroup.js +0 -29
@@ -19,6 +19,11 @@ export declare function useChatMessages(client: AcpClient | null, startSession:
19
19
  batchId?: string | undefined;
20
20
  prettyName?: string | undefined;
21
21
  icon?: string | undefined;
22
+ verbiage?: {
23
+ active: string;
24
+ past: string;
25
+ paramKey?: string | undefined;
26
+ } | undefined;
22
27
  subline?: string | undefined;
23
28
  contentPosition?: number | undefined;
24
29
  locations?: {
@@ -17,6 +17,11 @@ export declare function useToolCalls(client: AcpClient | null): {
17
17
  batchId?: string | undefined;
18
18
  prettyName?: string | undefined;
19
19
  icon?: string | undefined;
20
+ verbiage?: {
21
+ active: string;
22
+ past: string;
23
+ paramKey?: string | undefined;
24
+ } | undefined;
20
25
  subline?: string | undefined;
21
26
  contentPosition?: number | undefined;
22
27
  locations?: {
@@ -1,5 +1,7 @@
1
+ import { createLogger } from "@townco/core";
1
2
  import { useEffect } from "react";
2
3
  import { useChatStore } from "../store/chat-store.js";
4
+ const logger = createLogger("use-tool-calls", "debug");
3
5
  /**
4
6
  * Hook to track and manage tool calls from ACP sessions
5
7
  *
@@ -20,7 +22,6 @@ export function useToolCalls(client) {
20
22
  // Subscribe to session updates for tool calls
21
23
  const unsubscribe = client.onSessionUpdate((update) => {
22
24
  if (update.type === "tool_call") {
23
- // Initial tool call notification
24
25
  // Add to session-level tool calls (for sidebar)
25
26
  addToolCall(update.sessionId, update.toolCall);
26
27
  // Also add to current assistant message (for inline display)
@@ -31,6 +31,11 @@ export declare const DisplayMessage: z.ZodObject<{
31
31
  title: z.ZodString;
32
32
  prettyName: z.ZodOptional<z.ZodString>;
33
33
  icon: z.ZodOptional<z.ZodString>;
34
+ verbiage: z.ZodOptional<z.ZodObject<{
35
+ active: z.ZodString;
36
+ past: z.ZodString;
37
+ paramKey: z.ZodOptional<z.ZodString>;
38
+ }, z.core.$strip>>;
34
39
  subline: z.ZodOptional<z.ZodString>;
35
40
  kind: z.ZodEnum<{
36
41
  read: "read";
@@ -248,6 +253,11 @@ export declare const ChatSessionState: z.ZodObject<{
248
253
  title: z.ZodString;
249
254
  prettyName: z.ZodOptional<z.ZodString>;
250
255
  icon: z.ZodOptional<z.ZodString>;
256
+ verbiage: z.ZodOptional<z.ZodObject<{
257
+ active: z.ZodString;
258
+ past: z.ZodString;
259
+ paramKey: z.ZodOptional<z.ZodString>;
260
+ }, z.core.$strip>>;
251
261
  subline: z.ZodOptional<z.ZodString>;
252
262
  kind: z.ZodEnum<{
253
263
  read: "read";
@@ -273,6 +273,11 @@ export declare const ToolCallSchema: z.ZodObject<{
273
273
  title: z.ZodString;
274
274
  prettyName: z.ZodOptional<z.ZodString>;
275
275
  icon: z.ZodOptional<z.ZodString>;
276
+ verbiage: z.ZodOptional<z.ZodObject<{
277
+ active: z.ZodString;
278
+ past: z.ZodString;
279
+ paramKey: z.ZodOptional<z.ZodString>;
280
+ }, z.core.$strip>>;
276
281
  subline: z.ZodOptional<z.ZodString>;
277
282
  kind: z.ZodEnum<{
278
283
  read: "read";
@@ -498,6 +503,97 @@ export declare const ToolCallUpdateSchema: z.ZodObject<{
498
503
  }, z.core.$strip>>;
499
504
  subagentPort: z.ZodOptional<z.ZodNumber>;
500
505
  subagentSessionId: z.ZodOptional<z.ZodString>;
506
+ subagentMessages: z.ZodOptional<z.ZodArray<z.ZodObject<{
507
+ id: z.ZodString;
508
+ content: z.ZodString;
509
+ toolCalls: z.ZodOptional<z.ZodArray<z.ZodObject<{
510
+ id: z.ZodString;
511
+ title: z.ZodString;
512
+ prettyName: z.ZodOptional<z.ZodString>;
513
+ icon: z.ZodOptional<z.ZodString>;
514
+ status: z.ZodEnum<{
515
+ pending: "pending";
516
+ in_progress: "in_progress";
517
+ completed: "completed";
518
+ failed: "failed";
519
+ }>;
520
+ content: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
521
+ type: z.ZodLiteral<"content">;
522
+ content: z.ZodObject<{
523
+ type: z.ZodLiteral<"text">;
524
+ text: z.ZodString;
525
+ }, z.core.$strip>;
526
+ }, z.core.$strip>, z.ZodObject<{
527
+ type: z.ZodLiteral<"text">;
528
+ text: z.ZodString;
529
+ }, z.core.$strip>, z.ZodObject<{
530
+ type: z.ZodLiteral<"image">;
531
+ data: z.ZodString;
532
+ mimeType: z.ZodOptional<z.ZodString>;
533
+ alt: z.ZodOptional<z.ZodString>;
534
+ }, z.core.$strip>, z.ZodObject<{
535
+ type: z.ZodLiteral<"image">;
536
+ url: z.ZodString;
537
+ alt: z.ZodOptional<z.ZodString>;
538
+ }, z.core.$strip>, z.ZodObject<{
539
+ type: z.ZodLiteral<"diff">;
540
+ path: z.ZodString;
541
+ oldText: z.ZodString;
542
+ newText: z.ZodString;
543
+ line: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
544
+ }, z.core.$strip>, z.ZodObject<{
545
+ type: z.ZodLiteral<"terminal">;
546
+ terminalId: z.ZodString;
547
+ }, z.core.$strip>], "type">>>;
548
+ }, z.core.$strip>>>;
549
+ contentBlocks: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
550
+ type: z.ZodLiteral<"text">;
551
+ text: z.ZodString;
552
+ }, z.core.$strip>, z.ZodObject<{
553
+ type: z.ZodLiteral<"tool_call">;
554
+ toolCall: z.ZodObject<{
555
+ id: z.ZodString;
556
+ title: z.ZodString;
557
+ prettyName: z.ZodOptional<z.ZodString>;
558
+ icon: z.ZodOptional<z.ZodString>;
559
+ status: z.ZodEnum<{
560
+ pending: "pending";
561
+ in_progress: "in_progress";
562
+ completed: "completed";
563
+ failed: "failed";
564
+ }>;
565
+ content: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
566
+ type: z.ZodLiteral<"content">;
567
+ content: z.ZodObject<{
568
+ type: z.ZodLiteral<"text">;
569
+ text: z.ZodString;
570
+ }, z.core.$strip>;
571
+ }, z.core.$strip>, z.ZodObject<{
572
+ type: z.ZodLiteral<"text">;
573
+ text: z.ZodString;
574
+ }, z.core.$strip>, z.ZodObject<{
575
+ type: z.ZodLiteral<"image">;
576
+ data: z.ZodString;
577
+ mimeType: z.ZodOptional<z.ZodString>;
578
+ alt: z.ZodOptional<z.ZodString>;
579
+ }, z.core.$strip>, z.ZodObject<{
580
+ type: z.ZodLiteral<"image">;
581
+ url: z.ZodString;
582
+ alt: z.ZodOptional<z.ZodString>;
583
+ }, z.core.$strip>, z.ZodObject<{
584
+ type: z.ZodLiteral<"diff">;
585
+ path: z.ZodString;
586
+ oldText: z.ZodString;
587
+ newText: z.ZodString;
588
+ line: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
589
+ }, z.core.$strip>, z.ZodObject<{
590
+ type: z.ZodLiteral<"terminal">;
591
+ terminalId: z.ZodString;
592
+ }, z.core.$strip>], "type">>>;
593
+ }, z.core.$strip>;
594
+ }, z.core.$strip>], "type">>>;
595
+ isStreaming: z.ZodOptional<z.ZodBoolean>;
596
+ }, z.core.$strip>>>;
501
597
  }, z.core.$strip>;
502
598
  export type ToolCallUpdate = z.infer<typeof ToolCallUpdateSchema>;
503
599
  /**
@@ -132,6 +132,14 @@ export const ToolCallSchema = z.object({
132
132
  prettyName: z.string().optional(),
133
133
  /** Optional icon identifier for the tool (e.g. "Globe", "Search", "Edit") */
134
134
  icon: z.string().optional(),
135
+ /** Optional verbiage templates for displaying the tool call in different tenses */
136
+ verbiage: z
137
+ .object({
138
+ active: z.string(),
139
+ past: z.string(),
140
+ paramKey: z.string().optional(),
141
+ })
142
+ .optional(),
135
143
  /** Optional subline text to display below the tool title (e.g. current task status) */
136
144
  subline: z.string().optional(),
137
145
  /** Category for UI presentation */
@@ -195,6 +203,8 @@ export const ToolCallUpdateSchema = z.object({
195
203
  subagentPort: z.number().optional(),
196
204
  /** Sub-agent session ID for SSE connection */
197
205
  subagentSessionId: z.string().optional(),
206
+ /** Sub-agent messages for replay */
207
+ subagentMessages: z.array(SubagentMessageSchema).optional(),
198
208
  });
199
209
  /**
200
210
  * Helper to merge a tool call update into an existing tool call
@@ -220,6 +230,8 @@ export function mergeToolCallUpdate(existing, update) {
220
230
  // Sub-agent connection info
221
231
  subagentPort: update.subagentPort ?? existing.subagentPort,
222
232
  subagentSessionId: update.subagentSessionId ?? existing.subagentSessionId,
233
+ // Sub-agent messages for replay
234
+ subagentMessages: update.subagentMessages ?? existing.subagentMessages,
223
235
  };
224
236
  return merged;
225
237
  }
@@ -0,0 +1,30 @@
1
+ import type { ToolCall } from "../schemas/tool-call.js";
2
+ /**
3
+ * Display state for tool calls
4
+ * Maps to the UI presentation state
5
+ */
6
+ export type ToolCallDisplayState = "selecting" | "executing" | "completed" | "failed";
7
+ /**
8
+ * Determine if a tool call is preliminary (not yet fully invoked)
9
+ */
10
+ export declare function isPreliminaryToolCall(toolCall: ToolCall): boolean;
11
+ /**
12
+ * Get the display state for a tool call
13
+ */
14
+ export declare function getToolCallDisplayState(toolCall: ToolCall): ToolCallDisplayState;
15
+ /**
16
+ * Get verbiage based on display state
17
+ */
18
+ export declare function getToolCallStateVerbiage(toolCall: ToolCall): string;
19
+ /**
20
+ * Check if a group of tool calls are all completed
21
+ */
22
+ export declare function areAllToolCallsCompleted(toolCalls: ToolCall[]): boolean;
23
+ /**
24
+ * Check if any tool call in a group has failed
25
+ */
26
+ export declare function hasAnyToolCallFailed(toolCalls: ToolCall[]): boolean;
27
+ /**
28
+ * Get the overall state for a group of tool calls
29
+ */
30
+ export declare function getGroupDisplayState(toolCalls: ToolCall[]): ToolCallDisplayState;
@@ -0,0 +1,73 @@
1
+ import { getToolCallVerbiage } from "./tool-verbiage.js";
2
+ /**
3
+ * Determine if a tool call is preliminary (not yet fully invoked)
4
+ */
5
+ export function isPreliminaryToolCall(toolCall) {
6
+ return (toolCall.status === "pending" &&
7
+ (!toolCall.rawInput || Object.keys(toolCall.rawInput).length === 0));
8
+ }
9
+ /**
10
+ * Get the display state for a tool call
11
+ */
12
+ export function getToolCallDisplayState(toolCall) {
13
+ // Failed state
14
+ if (toolCall.status === "failed" || toolCall.error) {
15
+ return "failed";
16
+ }
17
+ // Completed state
18
+ if (toolCall.status === "completed") {
19
+ return "completed";
20
+ }
21
+ // Selecting state (preliminary - no parameters yet)
22
+ if (isPreliminaryToolCall(toolCall)) {
23
+ return "selecting";
24
+ }
25
+ // Executing state (pending or in_progress with parameters)
26
+ if (toolCall.status === "pending" || toolCall.status === "in_progress") {
27
+ return "executing";
28
+ }
29
+ // Default to executing
30
+ return "executing";
31
+ }
32
+ /**
33
+ * Get verbiage based on display state
34
+ */
35
+ export function getToolCallStateVerbiage(toolCall) {
36
+ const displayState = getToolCallDisplayState(toolCall);
37
+ switch (displayState) {
38
+ case "selecting":
39
+ return `Selecting ${toolCall.prettyName || toolCall.title}`;
40
+ case "executing":
41
+ return getToolCallVerbiage(toolCall, "active");
42
+ case "completed":
43
+ return getToolCallVerbiage(toolCall, "past");
44
+ case "failed":
45
+ return `Failed to use ${toolCall.prettyName || toolCall.title}`;
46
+ default:
47
+ return toolCall.prettyName || toolCall.title;
48
+ }
49
+ }
50
+ /**
51
+ * Check if a group of tool calls are all completed
52
+ */
53
+ export function areAllToolCallsCompleted(toolCalls) {
54
+ return toolCalls.every((tc) => getToolCallDisplayState(tc) === "completed");
55
+ }
56
+ /**
57
+ * Check if any tool call in a group has failed
58
+ */
59
+ export function hasAnyToolCallFailed(toolCalls) {
60
+ return toolCalls.some((tc) => getToolCallDisplayState(tc) === "failed");
61
+ }
62
+ /**
63
+ * Get the overall state for a group of tool calls
64
+ */
65
+ export function getGroupDisplayState(toolCalls) {
66
+ if (hasAnyToolCallFailed(toolCalls))
67
+ return "failed";
68
+ if (areAllToolCallsCompleted(toolCalls))
69
+ return "completed";
70
+ if (toolCalls.every((tc) => isPreliminaryToolCall(tc)))
71
+ return "selecting";
72
+ return "executing";
73
+ }
@@ -0,0 +1,13 @@
1
+ import type { ToolCall } from "../schemas/tool-call.js";
2
+ /**
3
+ * Generate a smart summary for a group of parallel tool calls
4
+ */
5
+ export declare function generateSmartSummary(toolCalls: ToolCall[], tense: "active" | "past"): string;
6
+ /**
7
+ * Get a short summary for display in collapsed state
8
+ */
9
+ export declare function getCollapsedSummary(toolCalls: ToolCall[]): string;
10
+ /**
11
+ * Get a summary for display in expanded/active state
12
+ */
13
+ export declare function getActiveSummary(toolCalls: ToolCall[]): string;
@@ -0,0 +1,172 @@
1
+ import { getToolCallVerbiage, getToolDisplayName } from "./tool-verbiage.js";
2
+ /**
3
+ * Extract a readable parameter value for summarization
4
+ */
5
+ function extractKeyParameter(toolCall) {
6
+ if (!toolCall.rawInput)
7
+ return null;
8
+ // Common parameter names to check, in order of preference
9
+ const paramKeys = [
10
+ "query",
11
+ "search_term",
12
+ "pattern",
13
+ "target_file",
14
+ "file_path",
15
+ "command",
16
+ "target_directory",
17
+ ];
18
+ for (const key of paramKeys) {
19
+ const value = toolCall.rawInput[key];
20
+ if (typeof value === "string" && value.trim()) {
21
+ return value.trim();
22
+ }
23
+ }
24
+ // Check subline as fallback
25
+ if (toolCall.subline) {
26
+ return toolCall.subline;
27
+ }
28
+ return null;
29
+ }
30
+ /**
31
+ * Truncate a list of items with "and N more" suffix
32
+ */
33
+ function formatItemList(items, maxItems = 3) {
34
+ if (items.length === 0)
35
+ return "";
36
+ if (items.length === 1)
37
+ return items[0];
38
+ if (items.length <= maxItems) {
39
+ return items.slice(0, -1).join(", ") + " and " + items[items.length - 1];
40
+ }
41
+ const shown = items.slice(0, maxItems);
42
+ const remaining = items.length - maxItems;
43
+ return shown.join(", ") + ` and ${remaining} more`;
44
+ }
45
+ /**
46
+ * Detect common patterns in file paths
47
+ */
48
+ function detectFilePattern(paths) {
49
+ if (paths.length === 0)
50
+ return null;
51
+ // Extract directory from first path
52
+ const firstPath = paths[0];
53
+ const lastSlash = firstPath.lastIndexOf("/");
54
+ if (lastSlash === -1)
55
+ return null;
56
+ const directory = firstPath.substring(0, lastSlash);
57
+ // Check if all paths are in the same directory
58
+ const allInSameDir = paths.every((p) => p.startsWith(directory + "/"));
59
+ if (allInSameDir) {
60
+ return directory;
61
+ }
62
+ return null;
63
+ }
64
+ /**
65
+ * Generate a smart summary for multiple tool calls of the same type
66
+ */
67
+ function generateSameToolSummary(toolCalls, tense) {
68
+ if (toolCalls.length === 0)
69
+ return "";
70
+ if (toolCalls.length === 1) {
71
+ return getToolCallVerbiage(toolCalls[0], tense);
72
+ }
73
+ const firstTool = toolCalls[0];
74
+ const toolName = firstTool.title;
75
+ // Extract parameters from all tool calls
76
+ const parameters = toolCalls
77
+ .map(extractKeyParameter)
78
+ .filter((p) => p !== null);
79
+ // Special handling for file operations
80
+ // Support both Town SDK tool names and legacy/Cursor tool names
81
+ const isFileOperation = toolName === "Read" ||
82
+ toolName === "Write" ||
83
+ toolName === "read_file" ||
84
+ toolName === "write" ||
85
+ toolName === "search_replace" ||
86
+ toolName === "delete_file";
87
+ if (isFileOperation) {
88
+ if (parameters.length > 0) {
89
+ const directory = detectFilePattern(parameters);
90
+ if (directory) {
91
+ const verb = tense === "active" ? "working on" : "worked on";
92
+ return `${verb.charAt(0).toUpperCase() + verb.slice(1)} ${parameters.length} files in ${directory}`;
93
+ }
94
+ const verb = toolName === "Read" || toolName === "read_file"
95
+ ? tense === "active"
96
+ ? "Reading"
97
+ : "Read"
98
+ : toolName === "Write" || toolName === "write"
99
+ ? tense === "active"
100
+ ? "Writing"
101
+ : "Wrote"
102
+ : toolName === "search_replace"
103
+ ? tense === "active"
104
+ ? "Editing"
105
+ : "Edited"
106
+ : tense === "active"
107
+ ? "Deleting"
108
+ : "Deleted";
109
+ return `${verb} ${formatItemList(parameters)}`;
110
+ }
111
+ }
112
+ // Special handling for search operations
113
+ // Support both Town SDK tool names and legacy/Cursor tool names
114
+ const isSearchOperation = toolName === "WebSearch" ||
115
+ toolName === "Grep" ||
116
+ toolName === "web_search" ||
117
+ toolName === "codebase_search" ||
118
+ toolName === "grep";
119
+ if (isSearchOperation) {
120
+ if (parameters.length > 0) {
121
+ const verb = toolName === "WebSearch" || toolName === "web_search"
122
+ ? tense === "active"
123
+ ? "Searching the web for"
124
+ : "Searched the web for"
125
+ : toolName === "codebase_search"
126
+ ? tense === "active"
127
+ ? "Searching codebase for"
128
+ : "Searched codebase for"
129
+ : tense === "active"
130
+ ? "Searching for"
131
+ : "Searched for";
132
+ return `${verb} ${formatItemList(parameters)}`;
133
+ }
134
+ }
135
+ // Generic fallback: use tool display name
136
+ const displayName = getToolDisplayName(firstTool);
137
+ const verb = tense === "active" ? "Using" : "Used";
138
+ return `${verb} ${displayName} ${toolCalls.length} times`;
139
+ }
140
+ /**
141
+ * Generate a smart summary for a group of parallel tool calls
142
+ */
143
+ export function generateSmartSummary(toolCalls, tense) {
144
+ if (toolCalls.length === 0)
145
+ return "";
146
+ if (toolCalls.length === 1) {
147
+ return getToolCallVerbiage(toolCalls[0], tense);
148
+ }
149
+ // Check if all tool calls are of the same type
150
+ const toolNames = [...new Set(toolCalls.map((tc) => tc.title))];
151
+ if (toolNames.length === 1) {
152
+ return generateSameToolSummary(toolCalls, tense);
153
+ }
154
+ // Mixed tool types - show display names
155
+ const displayNames = toolCalls
156
+ .map((tc) => tc.prettyName || tc.title)
157
+ .filter((name, index, self) => self.indexOf(name) === index);
158
+ const verb = tense === "active" ? "Running" : "Ran";
159
+ return `${verb} ${formatItemList(displayNames)}`;
160
+ }
161
+ /**
162
+ * Get a short summary for display in collapsed state
163
+ */
164
+ export function getCollapsedSummary(toolCalls) {
165
+ return generateSmartSummary(toolCalls, "past");
166
+ }
167
+ /**
168
+ * Get a summary for display in expanded/active state
169
+ */
170
+ export function getActiveSummary(toolCalls) {
171
+ return generateSmartSummary(toolCalls, "active");
172
+ }
@@ -0,0 +1,28 @@
1
+ import type { ToolCall } from "../schemas/tool-call.js";
2
+ /**
3
+ * Tool-specific verbiage templates
4
+ * Format: {paramName} will be replaced with actual parameter values
5
+ */
6
+ export interface ToolVerbiageTemplate {
7
+ active: string;
8
+ past: string;
9
+ paramKey?: string;
10
+ }
11
+ /**
12
+ * Mapping of tool names to their verbiage templates
13
+ *
14
+ * NOTE: Built-in Town SDK tools now include verbiage metadata in their definitions.
15
+ * This map is kept as a fallback for:
16
+ * - Legacy/external tools (e.g., Cursor IDE tools)
17
+ * - Custom tools without verbiage metadata
18
+ * - Backward compatibility
19
+ */
20
+ export declare const TOOL_VERBIAGE_MAP: Record<string, ToolVerbiageTemplate>;
21
+ /**
22
+ * Get display verbiage for a tool call
23
+ */
24
+ export declare function getToolCallVerbiage(toolCall: ToolCall, tense: "active" | "past"): string;
25
+ /**
26
+ * Get a short display name for a tool (for grouping)
27
+ */
28
+ export declare function getToolDisplayName(toolCall: ToolCall): string;