@townco/ui 0.1.47 → 0.1.49
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/index.d.ts +1 -0
- package/dist/core/hooks/index.js +1 -0
- package/dist/core/hooks/use-chat-input.d.ts +2 -0
- package/dist/core/hooks/use-chat-input.js +11 -1
- package/dist/core/hooks/use-chat-messages.d.ts +22 -1
- package/dist/core/hooks/use-chat-messages.js +19 -4
- package/dist/core/hooks/use-chat-session.js +22 -0
- package/dist/core/hooks/use-message-history.d.ts +12 -0
- package/dist/core/hooks/use-message-history.js +113 -0
- package/dist/core/hooks/use-tool-calls.d.ts +11 -0
- package/dist/core/schemas/chat.d.ts +40 -0
- package/dist/core/schemas/chat.js +9 -0
- package/dist/core/schemas/tool-call.d.ts +34 -0
- package/dist/core/schemas/tool-call.js +27 -0
- package/dist/core/store/chat-store.d.ts +7 -0
- package/dist/core/store/chat-store.js +46 -0
- package/dist/gui/components/ChatEmptyState.d.ts +4 -0
- package/dist/gui/components/ChatEmptyState.js +2 -2
- package/dist/gui/components/ChatInput.d.ts +21 -1
- package/dist/gui/components/ChatInput.js +184 -7
- package/dist/gui/components/ChatLayout.d.ts +3 -2
- package/dist/gui/components/ChatLayout.js +7 -7
- package/dist/gui/components/ChatPanelTabContent.d.ts +17 -0
- package/dist/gui/components/ChatPanelTabContent.js +6 -5
- package/dist/gui/components/ChatView.d.ts +3 -1
- package/dist/gui/components/ChatView.js +81 -49
- package/dist/gui/components/ContextUsageButton.d.ts +2 -0
- package/dist/gui/components/ContextUsageButton.js +3 -1
- package/dist/gui/components/InvokingGroup.d.ts +9 -0
- package/dist/gui/components/InvokingGroup.js +16 -0
- package/dist/gui/components/MessageContent.js +122 -6
- package/dist/gui/components/PanelTabsHeader.d.ts +1 -1
- package/dist/gui/components/PanelTabsHeader.js +6 -1
- package/dist/gui/components/Response.js +2 -0
- package/dist/gui/components/Task.js +3 -3
- package/dist/gui/components/TodoListItem.js +1 -1
- package/dist/gui/components/ToolCall.js +57 -5
- package/dist/gui/components/ToolCallGroup.d.ts +8 -0
- package/dist/gui/components/ToolCallGroup.js +29 -0
- package/dist/gui/components/index.d.ts +1 -2
- package/dist/gui/components/index.js +1 -2
- package/dist/sdk/client/acp-client.d.ts +24 -1
- package/dist/sdk/client/acp-client.js +28 -7
- package/dist/sdk/schemas/message.d.ts +41 -9
- package/dist/sdk/schemas/message.js +15 -3
- package/dist/sdk/schemas/session.d.ts +75 -10
- package/dist/sdk/transports/http.d.ts +14 -0
- package/dist/sdk/transports/http.js +130 -36
- package/dist/sdk/transports/stdio.d.ts +14 -0
- package/dist/sdk/transports/stdio.js +27 -10
- package/dist/sdk/transports/types.d.ts +29 -0
- package/package.json +3 -3
- package/dist/core/hooks/index.d.ts.map +0 -1
- package/dist/core/hooks/index.js.map +0 -1
- package/dist/core/hooks/use-chat-input.d.ts.map +0 -1
- package/dist/core/hooks/use-chat-input.js.map +0 -1
- package/dist/core/hooks/use-chat-messages.d.ts.map +0 -1
- package/dist/core/hooks/use-chat-messages.js.map +0 -1
- package/dist/core/hooks/use-chat-session.d.ts.map +0 -1
- package/dist/core/hooks/use-chat-session.js.map +0 -1
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js.map +0 -1
- package/dist/core/lib/logger.d.ts +0 -24
- package/dist/core/lib/logger.js +0 -108
- package/dist/core/schemas/chat.d.ts.map +0 -1
- package/dist/core/schemas/chat.js.map +0 -1
- package/dist/core/schemas/index.d.ts.map +0 -1
- package/dist/core/schemas/index.js.map +0 -1
- package/dist/core/store/chat-store.d.ts.map +0 -1
- package/dist/core/store/chat-store.js.map +0 -1
- package/dist/gui/components/Button.d.ts.map +0 -1
- package/dist/gui/components/Button.js.map +0 -1
- package/dist/gui/components/Card.d.ts.map +0 -1
- package/dist/gui/components/Card.js.map +0 -1
- package/dist/gui/components/ChatInput.d.ts.map +0 -1
- package/dist/gui/components/ChatInput.js.map +0 -1
- package/dist/gui/components/ChatSecondaryPanel.d.ts.map +0 -1
- package/dist/gui/components/ChatSecondaryPanel.js.map +0 -1
- package/dist/gui/components/ChatStatus.d.ts.map +0 -1
- package/dist/gui/components/ChatStatus.js.map +0 -1
- package/dist/gui/components/Conversation.d.ts.map +0 -1
- package/dist/gui/components/Conversation.js.map +0 -1
- package/dist/gui/components/Dialog.d.ts.map +0 -1
- package/dist/gui/components/Dialog.js.map +0 -1
- package/dist/gui/components/HeightTransition.d.ts.map +0 -1
- package/dist/gui/components/HeightTransition.js.map +0 -1
- package/dist/gui/components/Input.d.ts.map +0 -1
- package/dist/gui/components/Input.js.map +0 -1
- package/dist/gui/components/Label.d.ts.map +0 -1
- package/dist/gui/components/Label.js.map +0 -1
- package/dist/gui/components/MarkdownRenderer.d.ts.map +0 -1
- package/dist/gui/components/MarkdownRenderer.js.map +0 -1
- package/dist/gui/components/Message.d.ts.map +0 -1
- package/dist/gui/components/Message.js.map +0 -1
- package/dist/gui/components/MessageContent.d.ts.map +0 -1
- package/dist/gui/components/MessageContent.js.map +0 -1
- package/dist/gui/components/MessageList.d.ts.map +0 -1
- package/dist/gui/components/MessageList.js.map +0 -1
- package/dist/gui/components/Reasoning.d.ts.map +0 -1
- package/dist/gui/components/Reasoning.js.map +0 -1
- package/dist/gui/components/Response.d.ts.map +0 -1
- package/dist/gui/components/Response.js.map +0 -1
- package/dist/gui/components/Select.d.ts.map +0 -1
- package/dist/gui/components/Select.js.map +0 -1
- package/dist/gui/components/Tabs.d.ts.map +0 -1
- package/dist/gui/components/Tabs.js.map +0 -1
- package/dist/gui/components/Task.d.ts.map +0 -1
- package/dist/gui/components/Task.js.map +0 -1
- package/dist/gui/components/Textarea.d.ts.map +0 -1
- package/dist/gui/components/Textarea.js.map +0 -1
- package/dist/gui/components/ThinkingBlock.d.ts.map +0 -1
- package/dist/gui/components/ThinkingBlock.js.map +0 -1
- package/dist/gui/components/TodoList.d.ts.map +0 -1
- package/dist/gui/components/TodoList.js.map +0 -1
- package/dist/gui/components/TodoListItem.d.ts.map +0 -1
- package/dist/gui/components/TodoListItem.js.map +0 -1
- package/dist/gui/components/index.d.ts.map +0 -1
- package/dist/gui/components/index.js.map +0 -1
- package/dist/gui/index.d.ts.map +0 -1
- package/dist/gui/index.js.map +0 -1
- package/dist/gui/lib/utils.d.ts.map +0 -1
- package/dist/gui/lib/utils.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/sdk/client/acp-client.d.ts.map +0 -1
- package/dist/sdk/client/acp-client.js.map +0 -1
- package/dist/sdk/client/index.d.ts.map +0 -1
- package/dist/sdk/client/index.js.map +0 -1
- package/dist/sdk/index.d.ts.map +0 -1
- package/dist/sdk/index.js.map +0 -1
- package/dist/sdk/schemas/agent.d.ts.map +0 -1
- package/dist/sdk/schemas/agent.js.map +0 -1
- package/dist/sdk/schemas/index.d.ts.map +0 -1
- package/dist/sdk/schemas/index.js.map +0 -1
- package/dist/sdk/schemas/message.d.ts.map +0 -1
- package/dist/sdk/schemas/message.js.map +0 -1
- package/dist/sdk/schemas/session.d.ts.map +0 -1
- package/dist/sdk/schemas/session.js.map +0 -1
- package/dist/sdk/transports/http.d.ts.map +0 -1
- package/dist/sdk/transports/http.js.map +0 -1
- package/dist/sdk/transports/index.d.ts.map +0 -1
- package/dist/sdk/transports/index.js.map +0 -1
- package/dist/sdk/transports/stdio.d.ts.map +0 -1
- package/dist/sdk/transports/stdio.js.map +0 -1
- package/dist/sdk/transports/types.d.ts.map +0 -1
- package/dist/sdk/transports/types.js.map +0 -1
- package/dist/sdk/transports/websocket.d.ts.map +0 -1
- package/dist/sdk/transports/websocket.js.map +0 -1
- package/dist/test-http-client.d.ts +0 -11
- package/dist/test-http-client.d.ts.map +0 -1
- package/dist/test-http-client.js +0 -147
- package/dist/test-http-client.js.map +0 -1
- package/dist/test-http-transport.d.ts +0 -11
- package/dist/test-http-transport.d.ts.map +0 -1
- package/dist/test-http-transport.js +0 -127
- package/dist/test-http-transport.js.map +0 -1
- package/dist/tui/components/ChatView.d.ts.map +0 -1
- package/dist/tui/components/ChatView.js.map +0 -1
- package/dist/tui/components/GameOfLife.d.ts.map +0 -1
- package/dist/tui/components/GameOfLife.js.map +0 -1
- package/dist/tui/components/InputBox.d.ts.map +0 -1
- package/dist/tui/components/InputBox.js.map +0 -1
- package/dist/tui/components/MessageList.d.ts.map +0 -1
- package/dist/tui/components/MessageList.js.map +0 -1
- package/dist/tui/components/ReadlineInput.d.ts.map +0 -1
- package/dist/tui/components/ReadlineInput.js.map +0 -1
- package/dist/tui/components/StatusBar.d.ts.map +0 -1
- package/dist/tui/components/StatusBar.js.map +0 -1
- package/dist/tui/components/index.d.ts.map +0 -1
- package/dist/tui/components/index.js.map +0 -1
- package/dist/tui/index.d.ts.map +0 -1
- package/dist/tui/index.js.map +0 -1
package/dist/core/hooks/index.js
CHANGED
|
@@ -10,6 +10,7 @@ export declare function useChatInput(client: AcpClient | null, startSession: ()
|
|
|
10
10
|
path: string;
|
|
11
11
|
size: number;
|
|
12
12
|
mimeType: string;
|
|
13
|
+
data: string;
|
|
13
14
|
}[];
|
|
14
15
|
onChange: (value: string) => void;
|
|
15
16
|
onSubmit: () => Promise<void>;
|
|
@@ -18,6 +19,7 @@ export declare function useChatInput(client: AcpClient | null, startSession: ()
|
|
|
18
19
|
path: string;
|
|
19
20
|
size: number;
|
|
20
21
|
mimeType: string;
|
|
22
|
+
data: string;
|
|
21
23
|
}) => void;
|
|
22
24
|
onRemoveFile: (index: number) => void;
|
|
23
25
|
};
|
|
@@ -27,11 +27,21 @@ export function useChatInput(client, startSession) {
|
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
29
|
const message = input.value;
|
|
30
|
+
const attachments = input.attachedFiles;
|
|
31
|
+
logger.debug("Submitting message with attachments", {
|
|
32
|
+
messageLength: message.length,
|
|
33
|
+
attachmentCount: attachments.length,
|
|
34
|
+
hasAttachments: attachments.length > 0,
|
|
35
|
+
});
|
|
30
36
|
// Clear input immediately for better UX
|
|
31
37
|
setInputValue("");
|
|
32
38
|
setInputSubmitting(true);
|
|
33
39
|
try {
|
|
34
|
-
await sendMessage(message);
|
|
40
|
+
await sendMessage(message, attachments.length > 0 ? attachments : undefined);
|
|
41
|
+
// Clear attachments after successful send
|
|
42
|
+
useChatStore.setState((state) => ({
|
|
43
|
+
input: { ...state.input, attachedFiles: [] },
|
|
44
|
+
}));
|
|
35
45
|
}
|
|
36
46
|
catch (error) {
|
|
37
47
|
// Error is handled in useChatMessages
|
|
@@ -16,8 +16,10 @@ export declare function useChatMessages(client: AcpClient | null, startSession:
|
|
|
16
16
|
title: string;
|
|
17
17
|
kind: "read" | "edit" | "delete" | "move" | "search" | "execute" | "think" | "fetch" | "switch_mode" | "other";
|
|
18
18
|
status: "pending" | "in_progress" | "completed" | "failed";
|
|
19
|
+
batchId?: string | undefined;
|
|
19
20
|
prettyName?: string | undefined;
|
|
20
21
|
icon?: string | undefined;
|
|
22
|
+
subline?: string | undefined;
|
|
21
23
|
contentPosition?: number | undefined;
|
|
22
24
|
locations?: {
|
|
23
25
|
path: string;
|
|
@@ -34,6 +36,15 @@ export declare function useChatMessages(client: AcpClient | null, startSession:
|
|
|
34
36
|
} | {
|
|
35
37
|
type: "text";
|
|
36
38
|
text: string;
|
|
39
|
+
} | {
|
|
40
|
+
type: "image";
|
|
41
|
+
data: string;
|
|
42
|
+
mimeType?: string | undefined;
|
|
43
|
+
alt?: string | undefined;
|
|
44
|
+
} | {
|
|
45
|
+
type: "image";
|
|
46
|
+
url: string;
|
|
47
|
+
alt?: string | undefined;
|
|
37
48
|
} | {
|
|
38
49
|
type: "diff";
|
|
39
50
|
path: string;
|
|
@@ -64,7 +75,17 @@ export declare function useChatMessages(client: AcpClient | null, startSession:
|
|
|
64
75
|
outputTokens?: number | undefined;
|
|
65
76
|
totalTokens?: number | undefined;
|
|
66
77
|
} | undefined;
|
|
78
|
+
images?: {
|
|
79
|
+
mimeType: string;
|
|
80
|
+
data: string;
|
|
81
|
+
}[] | undefined;
|
|
67
82
|
}[];
|
|
68
83
|
isStreaming: boolean;
|
|
69
|
-
sendMessage: (content: string
|
|
84
|
+
sendMessage: (content: string, attachments?: Array<{
|
|
85
|
+
name: string;
|
|
86
|
+
path: string;
|
|
87
|
+
size: number;
|
|
88
|
+
mimeType: string;
|
|
89
|
+
data: string;
|
|
90
|
+
}>) => Promise<void>;
|
|
70
91
|
};
|
|
@@ -18,7 +18,12 @@ export function useChatMessages(client, startSession) {
|
|
|
18
18
|
/**
|
|
19
19
|
* Send a message to the agent
|
|
20
20
|
*/
|
|
21
|
-
const sendMessage = useCallback(async (content) => {
|
|
21
|
+
const sendMessage = useCallback(async (content, attachments) => {
|
|
22
|
+
logger.debug("[sendMessage] Called with", {
|
|
23
|
+
contentLength: content.length,
|
|
24
|
+
attachmentsCount: attachments?.length || 0,
|
|
25
|
+
hasAttachments: !!attachments && attachments.length > 0,
|
|
26
|
+
});
|
|
22
27
|
if (!client) {
|
|
23
28
|
logger.error("No client available");
|
|
24
29
|
setError("No client available");
|
|
@@ -46,13 +51,21 @@ export function useChatMessages(client, startSession) {
|
|
|
46
51
|
const startTime = Date.now();
|
|
47
52
|
setIsStreaming(true);
|
|
48
53
|
setStreamingStartTime(startTime);
|
|
49
|
-
// Add user message to UI
|
|
54
|
+
// Add user message to UI with images
|
|
50
55
|
const userMessage = {
|
|
51
56
|
id: `msg_${Date.now()}_user`,
|
|
52
57
|
role: "user",
|
|
53
|
-
content,
|
|
58
|
+
content: content,
|
|
54
59
|
timestamp: new Date().toISOString(),
|
|
55
60
|
isStreaming: false,
|
|
61
|
+
// Include images for display
|
|
62
|
+
...(attachments && attachments.length > 0
|
|
63
|
+
? {
|
|
64
|
+
images: attachments
|
|
65
|
+
.filter((a) => a.mimeType.startsWith("image/"))
|
|
66
|
+
.map((a) => ({ mimeType: a.mimeType, data: a.data })),
|
|
67
|
+
}
|
|
68
|
+
: {}),
|
|
56
69
|
};
|
|
57
70
|
addMessage(userMessage);
|
|
58
71
|
// Create placeholder for assistant message BEFORE sending
|
|
@@ -69,7 +82,9 @@ export function useChatMessages(client, startSession) {
|
|
|
69
82
|
const messageStream = client.receiveMessages();
|
|
70
83
|
// Send ONLY the new message (not full history)
|
|
71
84
|
// The agent backend now manages conversation context
|
|
72
|
-
client
|
|
85
|
+
client
|
|
86
|
+
.sendMessage(content, activeSessionId, attachments)
|
|
87
|
+
.catch((error) => {
|
|
73
88
|
const message = error instanceof Error ? error.message : String(error);
|
|
74
89
|
setError(message);
|
|
75
90
|
setIsStreaming(false);
|
|
@@ -48,6 +48,26 @@ export function useChatSession(client, initialSessionId) {
|
|
|
48
48
|
return "";
|
|
49
49
|
})
|
|
50
50
|
.join("");
|
|
51
|
+
// Extract image blocks for user messages
|
|
52
|
+
const imageBlocks = [];
|
|
53
|
+
for (const c of update.message.content) {
|
|
54
|
+
if (c.type === "image") {
|
|
55
|
+
const imgBlock = c;
|
|
56
|
+
// Handle both formats: direct data/mimeType or source object
|
|
57
|
+
if (imgBlock.source?.data) {
|
|
58
|
+
imageBlocks.push({
|
|
59
|
+
mimeType: imgBlock.source.media_type || "image/png",
|
|
60
|
+
data: imgBlock.source.data,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
else if (imgBlock.data) {
|
|
64
|
+
imageBlocks.push({
|
|
65
|
+
mimeType: imgBlock.mimeType || "image/png",
|
|
66
|
+
data: imgBlock.data,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
51
71
|
// During session replay, chunks for the same message arrive separately
|
|
52
72
|
// Check if we should append to the last assistant message or create a new one
|
|
53
73
|
const messages = useChatStore.getState().messages;
|
|
@@ -70,6 +90,8 @@ export function useChatSession(client, initialSessionId) {
|
|
|
70
90
|
content: textContent,
|
|
71
91
|
timestamp: update.message.timestamp,
|
|
72
92
|
isStreaming: false,
|
|
93
|
+
// Include images if present (for user messages)
|
|
94
|
+
...(imageBlocks.length > 0 ? { images: imageBlocks } : {}),
|
|
73
95
|
};
|
|
74
96
|
addMessage(displayMessage);
|
|
75
97
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for managing message history with localStorage persistence.
|
|
3
|
+
* Allows navigating through previously sent messages with up/down arrow keys.
|
|
4
|
+
*/
|
|
5
|
+
export declare function useMessageHistory(): {
|
|
6
|
+
addToHistory: (message: string) => void;
|
|
7
|
+
navigatePrevious: (currentValue: string) => string | null;
|
|
8
|
+
navigateNext: () => string | null;
|
|
9
|
+
resetNavigation: () => void;
|
|
10
|
+
isBrowsingHistory: boolean;
|
|
11
|
+
historyIndex: number;
|
|
12
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { useCallback, useState } from "react";
|
|
2
|
+
const STORAGE_KEY = "town-message-history";
|
|
3
|
+
const MAX_HISTORY_SIZE = 100;
|
|
4
|
+
/**
|
|
5
|
+
* Hook for managing message history with localStorage persistence.
|
|
6
|
+
* Allows navigating through previously sent messages with up/down arrow keys.
|
|
7
|
+
*/
|
|
8
|
+
export function useMessageHistory() {
|
|
9
|
+
// Index into history, -1 means "not browsing history"
|
|
10
|
+
const [historyIndex, setHistoryIndex] = useState(-1);
|
|
11
|
+
// Draft message saved when user starts browsing history
|
|
12
|
+
const [draftMessage, setDraftMessage] = useState("");
|
|
13
|
+
/**
|
|
14
|
+
* Get all messages from history
|
|
15
|
+
*/
|
|
16
|
+
const getHistory = useCallback(() => {
|
|
17
|
+
try {
|
|
18
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
19
|
+
return stored ? JSON.parse(stored) : [];
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
}, []);
|
|
25
|
+
/**
|
|
26
|
+
* Add a message to history (called when message is sent)
|
|
27
|
+
*/
|
|
28
|
+
const addToHistory = useCallback((message) => {
|
|
29
|
+
const trimmed = message.trim();
|
|
30
|
+
if (!trimmed)
|
|
31
|
+
return;
|
|
32
|
+
try {
|
|
33
|
+
const history = getHistory();
|
|
34
|
+
// Don't add duplicate of most recent message
|
|
35
|
+
if (history[0] === trimmed)
|
|
36
|
+
return;
|
|
37
|
+
// Add to beginning of array (most recent first)
|
|
38
|
+
const newHistory = [trimmed, ...history].slice(0, MAX_HISTORY_SIZE);
|
|
39
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(newHistory));
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// Ignore localStorage errors
|
|
43
|
+
}
|
|
44
|
+
// Reset navigation state
|
|
45
|
+
setHistoryIndex(-1);
|
|
46
|
+
setDraftMessage("");
|
|
47
|
+
}, [getHistory]);
|
|
48
|
+
/**
|
|
49
|
+
* Navigate to previous message (up arrow)
|
|
50
|
+
* Returns the message to display, or null if no navigation happened
|
|
51
|
+
*/
|
|
52
|
+
const navigatePrevious = useCallback((currentValue) => {
|
|
53
|
+
const history = getHistory();
|
|
54
|
+
if (history.length === 0)
|
|
55
|
+
return null;
|
|
56
|
+
// If starting to browse history, save current input as draft
|
|
57
|
+
if (historyIndex === -1) {
|
|
58
|
+
setDraftMessage(currentValue);
|
|
59
|
+
}
|
|
60
|
+
const newIndex = Math.min(historyIndex + 1, history.length - 1);
|
|
61
|
+
if (newIndex === historyIndex)
|
|
62
|
+
return null; // Already at oldest
|
|
63
|
+
const message = history[newIndex];
|
|
64
|
+
if (message === undefined)
|
|
65
|
+
return null;
|
|
66
|
+
setHistoryIndex(newIndex);
|
|
67
|
+
return message;
|
|
68
|
+
}, [historyIndex, getHistory]);
|
|
69
|
+
/**
|
|
70
|
+
* Navigate to next message (down arrow)
|
|
71
|
+
* Returns the message to display, or null if no navigation happened
|
|
72
|
+
*/
|
|
73
|
+
const navigateNext = useCallback(() => {
|
|
74
|
+
// Not browsing history
|
|
75
|
+
if (historyIndex === -1)
|
|
76
|
+
return null;
|
|
77
|
+
const history = getHistory();
|
|
78
|
+
if (historyIndex === 0) {
|
|
79
|
+
// Return to draft
|
|
80
|
+
setHistoryIndex(-1);
|
|
81
|
+
const draft = draftMessage;
|
|
82
|
+
setDraftMessage("");
|
|
83
|
+
return draft;
|
|
84
|
+
}
|
|
85
|
+
const newIndex = historyIndex - 1;
|
|
86
|
+
const message = history[newIndex];
|
|
87
|
+
if (message === undefined)
|
|
88
|
+
return null;
|
|
89
|
+
setHistoryIndex(newIndex);
|
|
90
|
+
return message;
|
|
91
|
+
}, [historyIndex, draftMessage, getHistory]);
|
|
92
|
+
/**
|
|
93
|
+
* Reset history navigation (called when user types something new)
|
|
94
|
+
*/
|
|
95
|
+
const resetNavigation = useCallback(() => {
|
|
96
|
+
if (historyIndex !== -1) {
|
|
97
|
+
setHistoryIndex(-1);
|
|
98
|
+
setDraftMessage("");
|
|
99
|
+
}
|
|
100
|
+
}, [historyIndex]);
|
|
101
|
+
/**
|
|
102
|
+
* Check if currently browsing history
|
|
103
|
+
*/
|
|
104
|
+
const isBrowsingHistory = historyIndex !== -1;
|
|
105
|
+
return {
|
|
106
|
+
addToHistory,
|
|
107
|
+
navigatePrevious,
|
|
108
|
+
navigateNext,
|
|
109
|
+
resetNavigation,
|
|
110
|
+
isBrowsingHistory,
|
|
111
|
+
historyIndex,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
@@ -14,8 +14,10 @@ export declare function useToolCalls(client: AcpClient | null): {
|
|
|
14
14
|
title: string;
|
|
15
15
|
kind: "read" | "edit" | "delete" | "move" | "search" | "execute" | "think" | "fetch" | "switch_mode" | "other";
|
|
16
16
|
status: "pending" | "in_progress" | "completed" | "failed";
|
|
17
|
+
batchId?: string | undefined;
|
|
17
18
|
prettyName?: string | undefined;
|
|
18
19
|
icon?: string | undefined;
|
|
20
|
+
subline?: string | undefined;
|
|
19
21
|
contentPosition?: number | undefined;
|
|
20
22
|
locations?: {
|
|
21
23
|
path: string;
|
|
@@ -32,6 +34,15 @@ export declare function useToolCalls(client: AcpClient | null): {
|
|
|
32
34
|
} | {
|
|
33
35
|
type: "text";
|
|
34
36
|
text: string;
|
|
37
|
+
} | {
|
|
38
|
+
type: "image";
|
|
39
|
+
data: string;
|
|
40
|
+
mimeType?: string | undefined;
|
|
41
|
+
alt?: string | undefined;
|
|
42
|
+
} | {
|
|
43
|
+
type: "image";
|
|
44
|
+
url: string;
|
|
45
|
+
alt?: string | undefined;
|
|
35
46
|
} | {
|
|
36
47
|
type: "diff";
|
|
37
48
|
path: string;
|
|
@@ -2,6 +2,14 @@ import { z } from "zod";
|
|
|
2
2
|
/**
|
|
3
3
|
* Chat UI state schemas
|
|
4
4
|
*/
|
|
5
|
+
/**
|
|
6
|
+
* Image attachment for display
|
|
7
|
+
*/
|
|
8
|
+
export declare const DisplayImageAttachment: z.ZodObject<{
|
|
9
|
+
mimeType: z.ZodString;
|
|
10
|
+
data: z.ZodString;
|
|
11
|
+
}, z.core.$strip>;
|
|
12
|
+
export type DisplayImageAttachment = z.infer<typeof DisplayImageAttachment>;
|
|
5
13
|
/**
|
|
6
14
|
* Display message schema (UI representation of messages)
|
|
7
15
|
*/
|
|
@@ -19,9 +27,11 @@ export declare const DisplayMessage: z.ZodObject<{
|
|
|
19
27
|
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
20
28
|
toolCalls: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
21
29
|
id: z.ZodString;
|
|
30
|
+
batchId: z.ZodOptional<z.ZodString>;
|
|
22
31
|
title: z.ZodString;
|
|
23
32
|
prettyName: z.ZodOptional<z.ZodString>;
|
|
24
33
|
icon: z.ZodOptional<z.ZodString>;
|
|
34
|
+
subline: z.ZodOptional<z.ZodString>;
|
|
25
35
|
kind: z.ZodEnum<{
|
|
26
36
|
read: "read";
|
|
27
37
|
edit: "edit";
|
|
@@ -56,6 +66,15 @@ export declare const DisplayMessage: z.ZodObject<{
|
|
|
56
66
|
}, z.core.$strip>, z.ZodObject<{
|
|
57
67
|
type: z.ZodLiteral<"text">;
|
|
58
68
|
text: z.ZodString;
|
|
69
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
70
|
+
type: z.ZodLiteral<"image">;
|
|
71
|
+
data: z.ZodString;
|
|
72
|
+
mimeType: z.ZodOptional<z.ZodString>;
|
|
73
|
+
alt: z.ZodOptional<z.ZodString>;
|
|
74
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
75
|
+
type: z.ZodLiteral<"image">;
|
|
76
|
+
url: z.ZodString;
|
|
77
|
+
alt: z.ZodOptional<z.ZodString>;
|
|
59
78
|
}, z.core.$strip>, z.ZodObject<{
|
|
60
79
|
type: z.ZodLiteral<"diff">;
|
|
61
80
|
path: z.ZodString;
|
|
@@ -89,6 +108,10 @@ export declare const DisplayMessage: z.ZodObject<{
|
|
|
89
108
|
outputTokens: z.ZodOptional<z.ZodNumber>;
|
|
90
109
|
totalTokens: z.ZodOptional<z.ZodNumber>;
|
|
91
110
|
}, z.core.$strip>>;
|
|
111
|
+
images: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
112
|
+
mimeType: z.ZodString;
|
|
113
|
+
data: z.ZodString;
|
|
114
|
+
}, z.core.$strip>>>;
|
|
92
115
|
}, z.core.$strip>;
|
|
93
116
|
export type DisplayMessage = z.infer<typeof DisplayMessage>;
|
|
94
117
|
/**
|
|
@@ -102,6 +125,7 @@ export declare const InputState: z.ZodObject<{
|
|
|
102
125
|
path: z.ZodString;
|
|
103
126
|
size: z.ZodNumber;
|
|
104
127
|
mimeType: z.ZodString;
|
|
128
|
+
data: z.ZodString;
|
|
105
129
|
}, z.core.$strip>>;
|
|
106
130
|
}, z.core.$strip>;
|
|
107
131
|
export type InputState = z.infer<typeof InputState>;
|
|
@@ -126,9 +150,11 @@ export declare const ChatSessionState: z.ZodObject<{
|
|
|
126
150
|
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
127
151
|
toolCalls: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
128
152
|
id: z.ZodString;
|
|
153
|
+
batchId: z.ZodOptional<z.ZodString>;
|
|
129
154
|
title: z.ZodString;
|
|
130
155
|
prettyName: z.ZodOptional<z.ZodString>;
|
|
131
156
|
icon: z.ZodOptional<z.ZodString>;
|
|
157
|
+
subline: z.ZodOptional<z.ZodString>;
|
|
132
158
|
kind: z.ZodEnum<{
|
|
133
159
|
read: "read";
|
|
134
160
|
edit: "edit";
|
|
@@ -163,6 +189,15 @@ export declare const ChatSessionState: z.ZodObject<{
|
|
|
163
189
|
}, z.core.$strip>, z.ZodObject<{
|
|
164
190
|
type: z.ZodLiteral<"text">;
|
|
165
191
|
text: z.ZodString;
|
|
192
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
193
|
+
type: z.ZodLiteral<"image">;
|
|
194
|
+
data: z.ZodString;
|
|
195
|
+
mimeType: z.ZodOptional<z.ZodString>;
|
|
196
|
+
alt: z.ZodOptional<z.ZodString>;
|
|
197
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
198
|
+
type: z.ZodLiteral<"image">;
|
|
199
|
+
url: z.ZodString;
|
|
200
|
+
alt: z.ZodOptional<z.ZodString>;
|
|
166
201
|
}, z.core.$strip>, z.ZodObject<{
|
|
167
202
|
type: z.ZodLiteral<"diff">;
|
|
168
203
|
path: z.ZodString;
|
|
@@ -196,6 +231,10 @@ export declare const ChatSessionState: z.ZodObject<{
|
|
|
196
231
|
outputTokens: z.ZodOptional<z.ZodNumber>;
|
|
197
232
|
totalTokens: z.ZodOptional<z.ZodNumber>;
|
|
198
233
|
}, z.core.$strip>>;
|
|
234
|
+
images: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
235
|
+
mimeType: z.ZodString;
|
|
236
|
+
data: z.ZodString;
|
|
237
|
+
}, z.core.$strip>>>;
|
|
199
238
|
}, z.core.$strip>>;
|
|
200
239
|
input: z.ZodObject<{
|
|
201
240
|
value: z.ZodString;
|
|
@@ -205,6 +244,7 @@ export declare const ChatSessionState: z.ZodObject<{
|
|
|
205
244
|
path: z.ZodString;
|
|
206
245
|
size: z.ZodNumber;
|
|
207
246
|
mimeType: z.ZodString;
|
|
247
|
+
data: z.ZodString;
|
|
208
248
|
}, z.core.$strip>>;
|
|
209
249
|
}, z.core.$strip>;
|
|
210
250
|
error: z.ZodNullable<z.ZodString>;
|
|
@@ -3,6 +3,13 @@ import { TokenUsageSchema, ToolCallSchema } from "./tool-call.js";
|
|
|
3
3
|
/**
|
|
4
4
|
* Chat UI state schemas
|
|
5
5
|
*/
|
|
6
|
+
/**
|
|
7
|
+
* Image attachment for display
|
|
8
|
+
*/
|
|
9
|
+
export const DisplayImageAttachment = z.object({
|
|
10
|
+
mimeType: z.string(),
|
|
11
|
+
data: z.string(), // base64 encoded
|
|
12
|
+
});
|
|
6
13
|
/**
|
|
7
14
|
* Display message schema (UI representation of messages)
|
|
8
15
|
*/
|
|
@@ -16,6 +23,7 @@ export const DisplayMessage = z.object({
|
|
|
16
23
|
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
17
24
|
toolCalls: z.array(ToolCallSchema).optional(),
|
|
18
25
|
tokenUsage: TokenUsageSchema.optional(), // Token usage for this message
|
|
26
|
+
images: z.array(DisplayImageAttachment).optional(), // Image attachments for user messages
|
|
19
27
|
});
|
|
20
28
|
/**
|
|
21
29
|
* Input state schema
|
|
@@ -28,6 +36,7 @@ export const InputState = z.object({
|
|
|
28
36
|
path: z.string(),
|
|
29
37
|
size: z.number(),
|
|
30
38
|
mimeType: z.string(),
|
|
39
|
+
data: z.string(), // base64 encoded file data
|
|
31
40
|
})),
|
|
32
41
|
});
|
|
33
42
|
/**
|
|
@@ -54,6 +54,15 @@ export declare const ToolCallContentBlockSchema: z.ZodDiscriminatedUnion<[z.ZodO
|
|
|
54
54
|
}, z.core.$strip>, z.ZodObject<{
|
|
55
55
|
type: z.ZodLiteral<"text">;
|
|
56
56
|
text: z.ZodString;
|
|
57
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
58
|
+
type: z.ZodLiteral<"image">;
|
|
59
|
+
data: z.ZodString;
|
|
60
|
+
mimeType: z.ZodOptional<z.ZodString>;
|
|
61
|
+
alt: z.ZodOptional<z.ZodString>;
|
|
62
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
63
|
+
type: z.ZodLiteral<"image">;
|
|
64
|
+
url: z.ZodString;
|
|
65
|
+
alt: z.ZodOptional<z.ZodString>;
|
|
57
66
|
}, z.core.$strip>, z.ZodObject<{
|
|
58
67
|
type: z.ZodLiteral<"diff">;
|
|
59
68
|
path: z.ZodString;
|
|
@@ -70,9 +79,11 @@ export type ToolCallContentBlock = z.infer<typeof ToolCallContentBlockSchema>;
|
|
|
70
79
|
*/
|
|
71
80
|
export declare const ToolCallSchema: z.ZodObject<{
|
|
72
81
|
id: z.ZodString;
|
|
82
|
+
batchId: z.ZodOptional<z.ZodString>;
|
|
73
83
|
title: z.ZodString;
|
|
74
84
|
prettyName: z.ZodOptional<z.ZodString>;
|
|
75
85
|
icon: z.ZodOptional<z.ZodString>;
|
|
86
|
+
subline: z.ZodOptional<z.ZodString>;
|
|
76
87
|
kind: z.ZodEnum<{
|
|
77
88
|
read: "read";
|
|
78
89
|
edit: "edit";
|
|
@@ -107,6 +118,15 @@ export declare const ToolCallSchema: z.ZodObject<{
|
|
|
107
118
|
}, z.core.$strip>, z.ZodObject<{
|
|
108
119
|
type: z.ZodLiteral<"text">;
|
|
109
120
|
text: z.ZodString;
|
|
121
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
122
|
+
type: z.ZodLiteral<"image">;
|
|
123
|
+
data: z.ZodString;
|
|
124
|
+
mimeType: z.ZodOptional<z.ZodString>;
|
|
125
|
+
alt: z.ZodOptional<z.ZodString>;
|
|
126
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
127
|
+
type: z.ZodLiteral<"image">;
|
|
128
|
+
url: z.ZodString;
|
|
129
|
+
alt: z.ZodOptional<z.ZodString>;
|
|
110
130
|
}, z.core.$strip>, z.ZodObject<{
|
|
111
131
|
type: z.ZodLiteral<"diff">;
|
|
112
132
|
path: z.ZodString;
|
|
@@ -147,6 +167,11 @@ export declare const ToolCallUpdateSchema: z.ZodObject<{
|
|
|
147
167
|
completed: "completed";
|
|
148
168
|
failed: "failed";
|
|
149
169
|
}>>;
|
|
170
|
+
title: z.ZodOptional<z.ZodString>;
|
|
171
|
+
prettyName: z.ZodOptional<z.ZodString>;
|
|
172
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
173
|
+
batchId: z.ZodOptional<z.ZodString>;
|
|
174
|
+
rawInput: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
150
175
|
locations: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
151
176
|
path: z.ZodString;
|
|
152
177
|
line: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
@@ -161,6 +186,15 @@ export declare const ToolCallUpdateSchema: z.ZodObject<{
|
|
|
161
186
|
}, z.core.$strip>, z.ZodObject<{
|
|
162
187
|
type: z.ZodLiteral<"text">;
|
|
163
188
|
text: z.ZodString;
|
|
189
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
190
|
+
type: z.ZodLiteral<"image">;
|
|
191
|
+
data: z.ZodString;
|
|
192
|
+
mimeType: z.ZodOptional<z.ZodString>;
|
|
193
|
+
alt: z.ZodOptional<z.ZodString>;
|
|
194
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
195
|
+
type: z.ZodLiteral<"image">;
|
|
196
|
+
url: z.ZodString;
|
|
197
|
+
alt: z.ZodOptional<z.ZodString>;
|
|
164
198
|
}, z.core.$strip>, z.ZodObject<{
|
|
165
199
|
type: z.ZodLiteral<"diff">;
|
|
166
200
|
path: z.ZodString;
|
|
@@ -55,6 +55,19 @@ export const ToolCallContentBlockSchema = z.discriminatedUnion("type", [
|
|
|
55
55
|
type: z.literal("text"),
|
|
56
56
|
text: z.string(),
|
|
57
57
|
}),
|
|
58
|
+
// Image content block (base64)
|
|
59
|
+
z.object({
|
|
60
|
+
type: z.literal("image"),
|
|
61
|
+
data: z.string(), // Base64 encoded image data
|
|
62
|
+
mimeType: z.string().optional(),
|
|
63
|
+
alt: z.string().optional(),
|
|
64
|
+
}),
|
|
65
|
+
// Image URL content block
|
|
66
|
+
z.object({
|
|
67
|
+
type: z.literal("image"),
|
|
68
|
+
url: z.string(), // URL or file path to the image
|
|
69
|
+
alt: z.string().optional(),
|
|
70
|
+
}),
|
|
58
71
|
z.object({
|
|
59
72
|
type: z.literal("diff"),
|
|
60
73
|
path: z.string(),
|
|
@@ -73,12 +86,16 @@ export const ToolCallContentBlockSchema = z.discriminatedUnion("type", [
|
|
|
73
86
|
export const ToolCallSchema = z.object({
|
|
74
87
|
/** Unique identifier within the session */
|
|
75
88
|
id: z.string(),
|
|
89
|
+
/** Batch identifier for parallel tool calls - tool calls with the same batchId executed together */
|
|
90
|
+
batchId: z.string().optional(),
|
|
76
91
|
/** Human-readable description of the operation */
|
|
77
92
|
title: z.string(),
|
|
78
93
|
/** Optional pretty name for the tool (e.g. "Web Search" instead of "web_search") */
|
|
79
94
|
prettyName: z.string().optional(),
|
|
80
95
|
/** Optional icon identifier for the tool (e.g. "Globe", "Search", "Edit") */
|
|
81
96
|
icon: z.string().optional(),
|
|
97
|
+
/** Optional subline text to display below the tool title (e.g. current task status) */
|
|
98
|
+
subline: z.string().optional(),
|
|
82
99
|
/** Category for UI presentation */
|
|
83
100
|
kind: ToolCallKindSchema,
|
|
84
101
|
/** Current execution status */
|
|
@@ -117,6 +134,11 @@ export const ToolCallSchema = z.object({
|
|
|
117
134
|
export const ToolCallUpdateSchema = z.object({
|
|
118
135
|
id: z.string(),
|
|
119
136
|
status: ToolCallStatusSchema.optional(),
|
|
137
|
+
title: z.string().optional(),
|
|
138
|
+
prettyName: z.string().optional(),
|
|
139
|
+
icon: z.string().optional(),
|
|
140
|
+
batchId: z.string().optional(),
|
|
141
|
+
rawInput: z.record(z.string(), z.unknown()).optional(),
|
|
120
142
|
locations: z.array(FileLocationSchema).optional(),
|
|
121
143
|
rawOutput: z.record(z.string(), z.unknown()).optional(),
|
|
122
144
|
content: z.array(ToolCallContentBlockSchema).optional(),
|
|
@@ -132,6 +154,11 @@ export function mergeToolCallUpdate(existing, update) {
|
|
|
132
154
|
...existing,
|
|
133
155
|
// Only update fields that are defined in the update
|
|
134
156
|
status: update.status ?? existing.status,
|
|
157
|
+
title: update.title ?? existing.title,
|
|
158
|
+
prettyName: update.prettyName ?? existing.prettyName,
|
|
159
|
+
icon: update.icon ?? existing.icon,
|
|
160
|
+
batchId: update.batchId ?? existing.batchId,
|
|
161
|
+
rawInput: update.rawInput ?? existing.rawInput,
|
|
135
162
|
locations: update.locations ?? existing.locations,
|
|
136
163
|
rawOutput: update.rawOutput ?? existing.rawOutput,
|
|
137
164
|
content: update.content
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { type LogEntry } from "@townco/core";
|
|
2
|
+
import type { TodoItem } from "../../gui/components/TodoListItem.js";
|
|
2
3
|
import type { ConnectionStatus, DisplayMessage, InputState } from "../schemas/index.js";
|
|
3
4
|
import type { ToolCall, ToolCallUpdate } from "../schemas/tool-call.js";
|
|
5
|
+
/**
|
|
6
|
+
* Selector to get todos for the current session (memoized to prevent infinite loops)
|
|
7
|
+
*/
|
|
8
|
+
export declare const selectTodosForCurrentSession: (state: ChatStore) => TodoItem[];
|
|
4
9
|
/**
|
|
5
10
|
* Context size breakdown from agent
|
|
6
11
|
*/
|
|
7
12
|
export interface ContextSize {
|
|
8
13
|
systemPromptTokens: number;
|
|
14
|
+
toolOverheadTokens?: number;
|
|
15
|
+
mcpOverheadTokens?: number;
|
|
9
16
|
userMessagesTokens: number;
|
|
10
17
|
assistantMessagesTokens: number;
|
|
11
18
|
toolInputTokens: number;
|