@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.
- package/dist/core/hooks/use-chat-messages.d.ts +5 -0
- package/dist/core/hooks/use-tool-calls.d.ts +5 -0
- package/dist/core/hooks/use-tool-calls.js +2 -1
- package/dist/core/schemas/chat.d.ts +10 -0
- package/dist/core/schemas/tool-call.d.ts +96 -0
- package/dist/core/schemas/tool-call.js +12 -0
- package/dist/core/utils/tool-call-state.d.ts +30 -0
- package/dist/core/utils/tool-call-state.js +73 -0
- package/dist/core/utils/tool-summary.d.ts +13 -0
- package/dist/core/utils/tool-summary.js +172 -0
- package/dist/core/utils/tool-verbiage.d.ts +28 -0
- package/dist/core/utils/tool-verbiage.js +185 -0
- package/dist/gui/components/AppSidebar.d.ts +22 -0
- package/dist/gui/components/AppSidebar.js +22 -0
- package/dist/gui/components/ChatLayout.d.ts +5 -0
- package/dist/gui/components/ChatLayout.js +239 -132
- package/dist/gui/components/ChatView.js +42 -118
- package/dist/gui/components/MessageContent.js +199 -49
- package/dist/gui/components/SessionHistory.d.ts +10 -0
- package/dist/gui/components/SessionHistory.js +101 -0
- package/dist/gui/components/SessionHistoryItem.d.ts +11 -0
- package/dist/gui/components/SessionHistoryItem.js +24 -0
- package/dist/gui/components/Sheet.d.ts +25 -0
- package/dist/gui/components/Sheet.js +36 -0
- package/dist/gui/components/Sidebar.d.ts +65 -0
- package/dist/gui/components/Sidebar.js +231 -0
- package/dist/gui/components/SidebarToggle.d.ts +3 -0
- package/dist/gui/components/SidebarToggle.js +9 -0
- package/dist/gui/components/SubAgentDetails.d.ts +13 -6
- package/dist/gui/components/SubAgentDetails.js +29 -14
- package/dist/gui/components/ToolCallList.js +3 -3
- package/dist/gui/components/ToolOperation.d.ts +11 -0
- package/dist/gui/components/ToolOperation.js +289 -0
- package/dist/gui/components/WorkProgress.d.ts +20 -0
- package/dist/gui/components/WorkProgress.js +79 -0
- package/dist/gui/components/index.d.ts +8 -1
- package/dist/gui/components/index.js +9 -1
- package/dist/gui/hooks/index.d.ts +1 -0
- package/dist/gui/hooks/index.js +1 -0
- package/dist/gui/hooks/use-mobile.d.ts +1 -0
- package/dist/gui/hooks/use-mobile.js +15 -0
- package/dist/gui/index.d.ts +1 -0
- package/dist/gui/index.js +2 -0
- package/dist/gui/lib/motion.d.ts +55 -0
- package/dist/gui/lib/motion.js +217 -0
- package/dist/sdk/schemas/session.d.ts +102 -6
- package/dist/sdk/transports/http.js +105 -37
- package/dist/sdk/transports/types.d.ts +5 -0
- package/package.json +8 -7
- package/src/styles/global.css +128 -1
- package/dist/gui/components/InvokingGroup.d.ts +0 -9
- package/dist/gui/components/InvokingGroup.js +0 -16
- package/dist/gui/components/ToolCall.d.ts +0 -8
- package/dist/gui/components/ToolCall.js +0 -226
- package/dist/gui/components/ToolCallGroup.d.ts +0 -8
- 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;
|