@townco/ui 0.1.16 → 0.1.18

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 (212) hide show
  1. package/dist/core/hooks/use-chat-input.d.ts +17 -17
  2. package/dist/core/hooks/use-chat-input.js +55 -64
  3. package/dist/core/hooks/use-chat-messages.js +114 -121
  4. package/dist/core/hooks/use-chat-session.d.ts +1 -1
  5. package/dist/core/hooks/use-chat-session.js +80 -78
  6. package/dist/gui/components/Button.d.ts +7 -23
  7. package/dist/gui/components/Button.js +27 -40
  8. package/dist/gui/components/Card.d.ts +7 -26
  9. package/dist/gui/components/Card.js +8 -54
  10. package/dist/gui/components/ChatSecondaryPanel.d.ts +25 -14
  11. package/dist/gui/components/ChatSecondaryPanel.js +60 -115
  12. package/dist/gui/components/ChatStatus.d.ts +2 -4
  13. package/dist/gui/components/ChatStatus.js +34 -45
  14. package/dist/gui/components/Conversation.d.ts +14 -17
  15. package/dist/gui/components/Conversation.js +83 -143
  16. package/dist/gui/components/Dialog.d.ts +11 -57
  17. package/dist/gui/components/Dialog.js +8 -84
  18. package/dist/gui/components/HeightTransition.d.ts +7 -12
  19. package/dist/gui/components/HeightTransition.js +77 -88
  20. package/dist/gui/components/Input.d.ts +6 -13
  21. package/dist/gui/components/Input.js +16 -27
  22. package/dist/gui/components/Label.d.ts +1 -7
  23. package/dist/gui/components/Label.js +2 -12
  24. package/dist/gui/components/MarkdownRenderer.d.ts +4 -6
  25. package/dist/gui/components/MarkdownRenderer.js +81 -178
  26. package/dist/gui/components/MessageContent.d.ts +22 -29
  27. package/dist/gui/components/PanelTabsHeader.d.ts +17 -0
  28. package/dist/gui/components/PanelTabsHeader.js +31 -0
  29. package/dist/gui/components/Reasoning.d.ts +24 -30
  30. package/dist/gui/components/Reasoning.js +60 -187
  31. package/dist/gui/components/Response.d.ts +9 -11
  32. package/dist/gui/components/Response.js +90 -229
  33. package/dist/gui/components/Select.d.ts +10 -69
  34. package/dist/gui/components/Select.js +12 -118
  35. package/dist/gui/components/Tabs.d.ts +4 -24
  36. package/dist/gui/components/Tabs.js +4 -32
  37. package/dist/gui/components/Task.d.ts +24 -28
  38. package/dist/gui/components/Task.js +31 -164
  39. package/dist/gui/components/Textarea.d.ts +7 -15
  40. package/dist/gui/components/Textarea.js +46 -63
  41. package/dist/gui/components/ThinkingBlock.d.ts +10 -20
  42. package/dist/gui/components/ThinkingBlock.js +35 -134
  43. package/dist/gui/components/TodoList.d.ts +10 -12
  44. package/dist/gui/components/TodoList.js +7 -22
  45. package/dist/gui/components/TodoListItem.d.ts +6 -9
  46. package/dist/gui/components/TodoListItem.js +4 -18
  47. package/dist/gui/components/ToolCall.js +5 -5
  48. package/dist/gui/components/index.d.ts +1 -0
  49. package/dist/gui/components/index.js +1 -0
  50. package/dist/gui/lib/utils.js +1 -1
  51. package/dist/index.test.js +1 -0
  52. package/dist/sdk/client/acp-client.d.ts +76 -88
  53. package/dist/sdk/client/acp-client.js +217 -215
  54. package/dist/sdk/schemas/agent.d.ts +64 -111
  55. package/dist/sdk/schemas/agent.js +24 -24
  56. package/dist/sdk/schemas/message.d.ts +147 -245
  57. package/dist/sdk/schemas/message.js +40 -40
  58. package/dist/sdk/schemas/session.d.ts +6 -6
  59. package/dist/sdk/transports/http.d.ts +55 -55
  60. package/dist/sdk/transports/stdio.d.ts +20 -20
  61. package/dist/sdk/transports/types.d.ts +42 -42
  62. package/dist/sdk/transports/websocket.d.ts +12 -12
  63. package/dist/sdk/transports/websocket.js +46 -52
  64. package/dist/tui/components/ChatView.d.ts +2 -4
  65. package/dist/tui/components/GameOfLife.js +35 -64
  66. package/dist/tui/components/InputBox.d.ts +11 -18
  67. package/dist/tui/components/InputBox.js +10 -70
  68. package/dist/tui/components/ReadlineInput.d.ts +6 -12
  69. package/dist/tui/components/ReadlineInput.js +237 -252
  70. package/dist/tui/components/SingleSelect.d.ts +9 -15
  71. package/dist/tui/components/SingleSelect.js +43 -84
  72. package/dist/tui/components/StatusBar.d.ts +6 -11
  73. package/dist/tui/components/StatusBar.js +67 -102
  74. package/package.json +3 -3
  75. package/dist/core/hooks/index.d.ts.map +0 -1
  76. package/dist/core/hooks/index.js.map +0 -1
  77. package/dist/core/hooks/use-chat-input.d.ts.map +0 -1
  78. package/dist/core/hooks/use-chat-input.js.map +0 -1
  79. package/dist/core/hooks/use-chat-messages.d.ts.map +0 -1
  80. package/dist/core/hooks/use-chat-messages.js.map +0 -1
  81. package/dist/core/hooks/use-chat-session.d.ts.map +0 -1
  82. package/dist/core/hooks/use-chat-session.js.map +0 -1
  83. package/dist/core/index.d.ts.map +0 -1
  84. package/dist/core/index.js.map +0 -1
  85. package/dist/core/schemas/chat.d.ts.map +0 -1
  86. package/dist/core/schemas/chat.js.map +0 -1
  87. package/dist/core/schemas/index.d.ts.map +0 -1
  88. package/dist/core/schemas/index.js.map +0 -1
  89. package/dist/core/store/chat-store.d.ts.map +0 -1
  90. package/dist/core/store/chat-store.js.map +0 -1
  91. package/dist/gui/components/Button.d.ts.map +0 -1
  92. package/dist/gui/components/Button.js.map +0 -1
  93. package/dist/gui/components/Card.d.ts.map +0 -1
  94. package/dist/gui/components/Card.js.map +0 -1
  95. package/dist/gui/components/ChatInput.d.ts.map +0 -1
  96. package/dist/gui/components/ChatInput.js.map +0 -1
  97. package/dist/gui/components/ChatInterface.d.ts +0 -12
  98. package/dist/gui/components/ChatInterface.d.ts.map +0 -1
  99. package/dist/gui/components/ChatInterface.js +0 -204
  100. package/dist/gui/components/ChatInterface.js.map +0 -1
  101. package/dist/gui/components/ChatPreview.d.ts +0 -12
  102. package/dist/gui/components/ChatPreview.d.ts.map +0 -1
  103. package/dist/gui/components/ChatPreview.js +0 -214
  104. package/dist/gui/components/ChatPreview.js.map +0 -1
  105. package/dist/gui/components/ChatSecondaryPanel.d.ts.map +0 -1
  106. package/dist/gui/components/ChatSecondaryPanel.js.map +0 -1
  107. package/dist/gui/components/ChatStatus.d.ts.map +0 -1
  108. package/dist/gui/components/ChatStatus.js.map +0 -1
  109. package/dist/gui/components/ChatView.d.ts +0 -8
  110. package/dist/gui/components/ChatView.d.ts.map +0 -1
  111. package/dist/gui/components/ChatView.js +0 -42
  112. package/dist/gui/components/ChatView.js.map +0 -1
  113. package/dist/gui/components/ConfigPanel.d.ts +0 -20
  114. package/dist/gui/components/ConfigPanel.d.ts.map +0 -1
  115. package/dist/gui/components/ConfigPanel.js +0 -225
  116. package/dist/gui/components/ConfigPanel.js.map +0 -1
  117. package/dist/gui/components/Conversation.d.ts.map +0 -1
  118. package/dist/gui/components/Conversation.js.map +0 -1
  119. package/dist/gui/components/Dialog.d.ts.map +0 -1
  120. package/dist/gui/components/Dialog.js.map +0 -1
  121. package/dist/gui/components/HeightTransition.d.ts.map +0 -1
  122. package/dist/gui/components/HeightTransition.js.map +0 -1
  123. package/dist/gui/components/Input.d.ts.map +0 -1
  124. package/dist/gui/components/Input.js.map +0 -1
  125. package/dist/gui/components/InputBox.d.ts +0 -21
  126. package/dist/gui/components/InputBox.d.ts.map +0 -1
  127. package/dist/gui/components/InputBox.js +0 -90
  128. package/dist/gui/components/InputBox.js.map +0 -1
  129. package/dist/gui/components/Label.d.ts.map +0 -1
  130. package/dist/gui/components/Label.js.map +0 -1
  131. package/dist/gui/components/MarkdownRenderer.d.ts.map +0 -1
  132. package/dist/gui/components/MarkdownRenderer.js.map +0 -1
  133. package/dist/gui/components/Message.d.ts.map +0 -1
  134. package/dist/gui/components/Message.js.map +0 -1
  135. package/dist/gui/components/MessageContent.d.ts.map +0 -1
  136. package/dist/gui/components/MessageContent.js.map +0 -1
  137. package/dist/gui/components/MessageList.d.ts.map +0 -1
  138. package/dist/gui/components/MessageList.js.map +0 -1
  139. package/dist/gui/components/PlaygroundLayout.d.ts +0 -14
  140. package/dist/gui/components/PlaygroundLayout.d.ts.map +0 -1
  141. package/dist/gui/components/PlaygroundLayout.js +0 -49
  142. package/dist/gui/components/PlaygroundLayout.js.map +0 -1
  143. package/dist/gui/components/Reasoning.d.ts.map +0 -1
  144. package/dist/gui/components/Reasoning.js.map +0 -1
  145. package/dist/gui/components/Response.d.ts.map +0 -1
  146. package/dist/gui/components/Response.js.map +0 -1
  147. package/dist/gui/components/Select.d.ts.map +0 -1
  148. package/dist/gui/components/Select.js.map +0 -1
  149. package/dist/gui/components/StatusBar.d.ts +0 -12
  150. package/dist/gui/components/StatusBar.d.ts.map +0 -1
  151. package/dist/gui/components/StatusBar.js +0 -58
  152. package/dist/gui/components/StatusBar.js.map +0 -1
  153. package/dist/gui/components/Tabs.d.ts.map +0 -1
  154. package/dist/gui/components/Tabs.js.map +0 -1
  155. package/dist/gui/components/Task.d.ts.map +0 -1
  156. package/dist/gui/components/Task.js.map +0 -1
  157. package/dist/gui/components/Textarea.d.ts.map +0 -1
  158. package/dist/gui/components/Textarea.js.map +0 -1
  159. package/dist/gui/components/ThinkingBlock.d.ts.map +0 -1
  160. package/dist/gui/components/ThinkingBlock.js.map +0 -1
  161. package/dist/gui/components/TodoList.d.ts.map +0 -1
  162. package/dist/gui/components/TodoList.js.map +0 -1
  163. package/dist/gui/components/TodoListItem.d.ts.map +0 -1
  164. package/dist/gui/components/TodoListItem.js.map +0 -1
  165. package/dist/gui/components/index.d.ts.map +0 -1
  166. package/dist/gui/components/index.js.map +0 -1
  167. package/dist/gui/index.d.ts.map +0 -1
  168. package/dist/gui/index.js.map +0 -1
  169. package/dist/gui/lib/utils.d.ts.map +0 -1
  170. package/dist/gui/lib/utils.js.map +0 -1
  171. package/dist/index.d.ts.map +0 -1
  172. package/dist/index.js.map +0 -1
  173. package/dist/sdk/client/acp-client.d.ts.map +0 -1
  174. package/dist/sdk/client/acp-client.js.map +0 -1
  175. package/dist/sdk/client/index.d.ts.map +0 -1
  176. package/dist/sdk/client/index.js.map +0 -1
  177. package/dist/sdk/index.d.ts.map +0 -1
  178. package/dist/sdk/index.js.map +0 -1
  179. package/dist/sdk/schemas/agent.d.ts.map +0 -1
  180. package/dist/sdk/schemas/agent.js.map +0 -1
  181. package/dist/sdk/schemas/index.d.ts.map +0 -1
  182. package/dist/sdk/schemas/index.js.map +0 -1
  183. package/dist/sdk/schemas/message.d.ts.map +0 -1
  184. package/dist/sdk/schemas/message.js.map +0 -1
  185. package/dist/sdk/schemas/session.d.ts.map +0 -1
  186. package/dist/sdk/schemas/session.js.map +0 -1
  187. package/dist/sdk/transports/http.d.ts.map +0 -1
  188. package/dist/sdk/transports/http.js.map +0 -1
  189. package/dist/sdk/transports/index.d.ts.map +0 -1
  190. package/dist/sdk/transports/index.js.map +0 -1
  191. package/dist/sdk/transports/stdio.d.ts.map +0 -1
  192. package/dist/sdk/transports/stdio.js.map +0 -1
  193. package/dist/sdk/transports/types.d.ts.map +0 -1
  194. package/dist/sdk/transports/types.js.map +0 -1
  195. package/dist/sdk/transports/websocket.d.ts.map +0 -1
  196. package/dist/sdk/transports/websocket.js.map +0 -1
  197. package/dist/tui/components/ChatView.d.ts.map +0 -1
  198. package/dist/tui/components/ChatView.js.map +0 -1
  199. package/dist/tui/components/GameOfLife.d.ts.map +0 -1
  200. package/dist/tui/components/GameOfLife.js.map +0 -1
  201. package/dist/tui/components/InputBox.d.ts.map +0 -1
  202. package/dist/tui/components/InputBox.js.map +0 -1
  203. package/dist/tui/components/MessageList.d.ts.map +0 -1
  204. package/dist/tui/components/MessageList.js.map +0 -1
  205. package/dist/tui/components/ReadlineInput.d.ts.map +0 -1
  206. package/dist/tui/components/ReadlineInput.js.map +0 -1
  207. package/dist/tui/components/StatusBar.d.ts.map +0 -1
  208. package/dist/tui/components/StatusBar.js.map +0 -1
  209. package/dist/tui/components/index.d.ts.map +0 -1
  210. package/dist/tui/components/index.js.map +0 -1
  211. package/dist/tui/index.d.ts.map +0 -1
  212. package/dist/tui/index.js.map +0 -1
@@ -3,21 +3,21 @@ import type { AcpClient } from "../../sdk/client/index.js";
3
3
  * Hook for managing chat input
4
4
  */
5
5
  export declare function useChatInput(client: AcpClient | null): {
6
- value: string;
7
- isSubmitting: boolean;
8
- attachedFiles: {
9
- name: string;
10
- path: string;
11
- size: number;
12
- mimeType: string;
13
- }[];
14
- onChange: (value: string) => void;
15
- onSubmit: () => Promise<void>;
16
- onAttachFile: (file: {
17
- name: string;
18
- path: string;
19
- size: number;
20
- mimeType: string;
21
- }) => void;
22
- onRemoveFile: (index: number) => void;
6
+ value: string;
7
+ isSubmitting: boolean;
8
+ attachedFiles: {
9
+ name: string;
10
+ path: string;
11
+ size: number;
12
+ mimeType: string;
13
+ }[];
14
+ onChange: (value: string) => void;
15
+ onSubmit: () => Promise<void>;
16
+ onAttachFile: (file: {
17
+ name: string;
18
+ path: string;
19
+ size: number;
20
+ mimeType: string;
21
+ }) => void;
22
+ onRemoveFile: (index: number) => void;
23
23
  };
@@ -5,68 +5,59 @@ import { useChatMessages } from "./use-chat-messages.js";
5
5
  * Hook for managing chat input
6
6
  */
7
7
  export function useChatInput(client) {
8
- const input = useChatStore((state) => state.input);
9
- const setInputValue = useChatStore((state) => state.setInputValue);
10
- const setInputSubmitting = useChatStore((state) => state.setInputSubmitting);
11
- const addFileAttachment = useChatStore((state) => state.addFileAttachment);
12
- const removeFileAttachment = useChatStore(
13
- (state) => state.removeFileAttachment,
14
- );
15
- const { sendMessage } = useChatMessages(client);
16
- /**
17
- * Handle input value change
18
- */
19
- const handleInputChange = useCallback(
20
- (value) => {
21
- setInputValue(value);
22
- },
23
- [setInputValue],
24
- );
25
- /**
26
- * Handle input submission
27
- */
28
- const handleSubmit = useCallback(async () => {
29
- if (!input.value.trim() || input.isSubmitting) {
30
- return;
31
- }
32
- const message = input.value;
33
- // Clear input immediately for better UX
34
- setInputValue("");
35
- setInputSubmitting(true);
36
- try {
37
- await sendMessage(message);
38
- } catch (error) {
39
- // Error is handled in useChatMessages
40
- console.error("Failed to send message:", error);
41
- } finally {
42
- setInputSubmitting(false);
43
- }
44
- }, [input, setInputValue, setInputSubmitting, sendMessage]);
45
- /**
46
- * Handle file attachment
47
- */
48
- const handleAttachFile = useCallback(
49
- (file) => {
50
- addFileAttachment(file);
51
- },
52
- [addFileAttachment],
53
- );
54
- /**
55
- * Handle file removal
56
- */
57
- const handleRemoveFile = useCallback(
58
- (index) => {
59
- removeFileAttachment(index);
60
- },
61
- [removeFileAttachment],
62
- );
63
- return {
64
- value: input.value,
65
- isSubmitting: input.isSubmitting,
66
- attachedFiles: input.attachedFiles,
67
- onChange: handleInputChange,
68
- onSubmit: handleSubmit,
69
- onAttachFile: handleAttachFile,
70
- onRemoveFile: handleRemoveFile,
71
- };
8
+ const input = useChatStore((state) => state.input);
9
+ const setInputValue = useChatStore((state) => state.setInputValue);
10
+ const setInputSubmitting = useChatStore((state) => state.setInputSubmitting);
11
+ const addFileAttachment = useChatStore((state) => state.addFileAttachment);
12
+ const removeFileAttachment = useChatStore((state) => state.removeFileAttachment);
13
+ const { sendMessage } = useChatMessages(client);
14
+ /**
15
+ * Handle input value change
16
+ */
17
+ const handleInputChange = useCallback((value) => {
18
+ setInputValue(value);
19
+ }, [setInputValue]);
20
+ /**
21
+ * Handle input submission
22
+ */
23
+ const handleSubmit = useCallback(async () => {
24
+ if (!input.value.trim() || input.isSubmitting) {
25
+ return;
26
+ }
27
+ const message = input.value;
28
+ // Clear input immediately for better UX
29
+ setInputValue("");
30
+ setInputSubmitting(true);
31
+ try {
32
+ await sendMessage(message);
33
+ }
34
+ catch (error) {
35
+ // Error is handled in useChatMessages
36
+ console.error("Failed to send message:", error);
37
+ }
38
+ finally {
39
+ setInputSubmitting(false);
40
+ }
41
+ }, [input, setInputValue, setInputSubmitting, sendMessage]);
42
+ /**
43
+ * Handle file attachment
44
+ */
45
+ const handleAttachFile = useCallback((file) => {
46
+ addFileAttachment(file);
47
+ }, [addFileAttachment]);
48
+ /**
49
+ * Handle file removal
50
+ */
51
+ const handleRemoveFile = useCallback((index) => {
52
+ removeFileAttachment(index);
53
+ }, [removeFileAttachment]);
54
+ return {
55
+ value: input.value,
56
+ isSubmitting: input.isSubmitting,
57
+ attachedFiles: input.attachedFiles,
58
+ onChange: handleInputChange,
59
+ onSubmit: handleSubmit,
60
+ onAttachFile: handleAttachFile,
61
+ onRemoveFile: handleRemoveFile,
62
+ };
72
63
  }
@@ -4,125 +4,118 @@ import { useChatStore } from "../store/chat-store.js";
4
4
  * Hook for managing chat messages
5
5
  */
6
6
  export function useChatMessages(client) {
7
- const messages = useChatStore((state) => state.messages);
8
- const isStreaming = useChatStore((state) => state.isStreaming);
9
- const sessionId = useChatStore((state) => state.sessionId);
10
- const setIsStreaming = useChatStore((state) => state.setIsStreaming);
11
- const setStreamingStartTime = useChatStore(
12
- (state) => state.setStreamingStartTime,
13
- );
14
- const addMessage = useChatStore((state) => state.addMessage);
15
- const updateMessage = useChatStore((state) => state.updateMessage);
16
- const setError = useChatStore((state) => state.setError);
17
- /**
18
- * Send a message to the agent
19
- */
20
- const sendMessage = useCallback(
21
- async (content) => {
22
- if (!client) {
23
- console.error("❌ No client available");
24
- setError("No client available");
25
- return;
26
- }
27
- if (!sessionId) {
28
- console.error("❌ No active session");
29
- setError("No active session");
30
- return;
31
- }
32
- try {
33
- // Start streaming and track time immediately
34
- const startTime = Date.now();
35
- setIsStreaming(true);
36
- setStreamingStartTime(startTime);
37
- // Add user message to UI
38
- const userMessage = {
39
- id: `msg_${Date.now()}_user`,
40
- role: "user",
41
- content,
42
- timestamp: new Date().toISOString(),
43
- isStreaming: false,
44
- };
45
- addMessage(userMessage);
46
- // Create placeholder for assistant message BEFORE sending
47
- const assistantMessageId = `msg_${Date.now()}_assistant`;
48
- const assistantMessage = {
49
- id: assistantMessageId,
50
- role: "assistant",
51
- content: "",
52
- timestamp: new Date().toISOString(),
53
- isStreaming: true,
54
- streamingStartTime: startTime, // Use the same start time from when user sent message
55
- };
56
- addMessage(assistantMessage);
57
- // Build full conversation history (excluding system messages)
58
- const conversationHistory = messages
59
- .filter((msg) => msg.role !== "system")
60
- .map(
61
- (msg) =>
62
- `${msg.role === "user" ? "Human" : "Assistant"}: ${msg.content}`,
63
- )
64
- .join("\n\n");
65
- // Combine history with new message
66
- const fullPrompt = conversationHistory
67
- ? `${conversationHistory}\n\nHuman: ${content}`
68
- : content;
69
- // Start receiving chunks (async iterator)
70
- const messageStream = client.receiveMessages();
71
- // Send full conversation without awaiting (fire and forget)
72
- // This allows chunks to start arriving while we're listening
73
- client.sendMessage(fullPrompt, sessionId).catch((error) => {
74
- const message =
75
- error instanceof Error ? error.message : String(error);
76
- setError(message);
77
- setIsStreaming(false);
78
- setStreamingStartTime(null);
79
- });
80
- // Listen for streaming chunks
81
- let accumulatedContent = "";
82
- for await (const chunk of messageStream) {
83
- if (chunk.isComplete) {
84
- // Update final message
85
- updateMessage(assistantMessageId, {
86
- content: accumulatedContent,
87
- isStreaming: false,
88
- streamingStartTime: undefined, // Clear streaming start time
89
- });
90
- setIsStreaming(false);
91
- setStreamingStartTime(null); // Clear global streaming start time
92
- break;
93
- } else {
94
- // Update streaming content
95
- if (chunk.contentDelta.type === "text") {
96
- accumulatedContent += chunk.contentDelta.text;
97
- updateMessage(assistantMessageId, {
98
- content: accumulatedContent,
99
- });
100
- // Small delay to allow Ink to render between chunks (~60fps)
101
- await new Promise((resolve) => setTimeout(resolve, 16));
102
- }
103
- }
104
- }
105
- } catch (error) {
106
- const message = error instanceof Error ? error.message : String(error);
107
- setError(message);
108
- setIsStreaming(false);
109
- setStreamingStartTime(null); // Clear streaming start time on error
110
- }
111
- },
112
- [
113
- client,
114
- sessionId,
115
- messages,
116
- addMessage,
117
- updateMessage,
118
- setIsStreaming,
119
- setStreamingStartTime,
120
- setError,
121
- ],
122
- );
123
- return {
124
- messages,
125
- isStreaming,
126
- sendMessage,
127
- };
7
+ const messages = useChatStore((state) => state.messages);
8
+ const isStreaming = useChatStore((state) => state.isStreaming);
9
+ const sessionId = useChatStore((state) => state.sessionId);
10
+ const setIsStreaming = useChatStore((state) => state.setIsStreaming);
11
+ const setStreamingStartTime = useChatStore((state) => state.setStreamingStartTime);
12
+ const addMessage = useChatStore((state) => state.addMessage);
13
+ const updateMessage = useChatStore((state) => state.updateMessage);
14
+ const setError = useChatStore((state) => state.setError);
15
+ /**
16
+ * Send a message to the agent
17
+ */
18
+ const sendMessage = useCallback(async (content) => {
19
+ if (!client) {
20
+ console.error("❌ No client available");
21
+ setError("No client available");
22
+ return;
23
+ }
24
+ if (!sessionId) {
25
+ console.error("❌ No active session");
26
+ setError("No active session");
27
+ return;
28
+ }
29
+ try {
30
+ // Start streaming and track time immediately
31
+ const startTime = Date.now();
32
+ setIsStreaming(true);
33
+ setStreamingStartTime(startTime);
34
+ // Add user message to UI
35
+ const userMessage = {
36
+ id: `msg_${Date.now()}_user`,
37
+ role: "user",
38
+ content,
39
+ timestamp: new Date().toISOString(),
40
+ isStreaming: false,
41
+ };
42
+ addMessage(userMessage);
43
+ // Create placeholder for assistant message BEFORE sending
44
+ const assistantMessageId = `msg_${Date.now()}_assistant`;
45
+ const assistantMessage = {
46
+ id: assistantMessageId,
47
+ role: "assistant",
48
+ content: "",
49
+ timestamp: new Date().toISOString(),
50
+ isStreaming: true,
51
+ streamingStartTime: startTime, // Use the same start time from when user sent message
52
+ };
53
+ addMessage(assistantMessage);
54
+ // Build full conversation history (excluding system messages)
55
+ const conversationHistory = messages
56
+ .filter((msg) => msg.role !== "system")
57
+ .map((msg) => `${msg.role === "user" ? "Human" : "Assistant"}: ${msg.content}`)
58
+ .join("\n\n");
59
+ // Combine history with new message
60
+ const fullPrompt = conversationHistory
61
+ ? `${conversationHistory}\n\nHuman: ${content}`
62
+ : content;
63
+ // Start receiving chunks (async iterator)
64
+ const messageStream = client.receiveMessages();
65
+ // Send full conversation without awaiting (fire and forget)
66
+ // This allows chunks to start arriving while we're listening
67
+ client.sendMessage(fullPrompt, sessionId).catch((error) => {
68
+ const message = error instanceof Error ? error.message : String(error);
69
+ setError(message);
70
+ setIsStreaming(false);
71
+ setStreamingStartTime(null);
72
+ });
73
+ // Listen for streaming chunks
74
+ let accumulatedContent = "";
75
+ for await (const chunk of messageStream) {
76
+ if (chunk.isComplete) {
77
+ // Update final message
78
+ updateMessage(assistantMessageId, {
79
+ content: accumulatedContent,
80
+ isStreaming: false,
81
+ streamingStartTime: undefined, // Clear streaming start time
82
+ });
83
+ setIsStreaming(false);
84
+ setStreamingStartTime(null); // Clear global streaming start time
85
+ break;
86
+ }
87
+ else {
88
+ // Update streaming content
89
+ if (chunk.contentDelta.type === "text") {
90
+ accumulatedContent += chunk.contentDelta.text;
91
+ updateMessage(assistantMessageId, {
92
+ content: accumulatedContent,
93
+ });
94
+ // Small delay to allow Ink to render between chunks (~60fps)
95
+ await new Promise((resolve) => setTimeout(resolve, 16));
96
+ }
97
+ }
98
+ }
99
+ }
100
+ catch (error) {
101
+ const message = error instanceof Error ? error.message : String(error);
102
+ setError(message);
103
+ setIsStreaming(false);
104
+ setStreamingStartTime(null); // Clear streaming start time on error
105
+ }
106
+ }, [
107
+ client,
108
+ sessionId,
109
+ messages,
110
+ addMessage,
111
+ updateMessage,
112
+ setIsStreaming,
113
+ setStreamingStartTime,
114
+ setError,
115
+ ]);
116
+ return {
117
+ messages,
118
+ isStreaming,
119
+ sendMessage,
120
+ };
128
121
  }
@@ -3,7 +3,7 @@ import type { AcpClient } from "../../sdk/client/index.js";
3
3
  * Hook for managing chat session lifecycle
4
4
  */
5
5
  export declare function useChatSession(client: AcpClient | null): {
6
- connectionStatus: "connecting" | "connected" | "error" | "disconnected";
6
+ connectionStatus: "error" | "connecting" | "connected" | "disconnected";
7
7
  sessionId: string | null;
8
8
  connect: () => Promise<void>;
9
9
  startSession: () => Promise<void>;
@@ -4,82 +4,84 @@ import { useChatStore } from "../store/chat-store.js";
4
4
  * Hook for managing chat session lifecycle
5
5
  */
6
6
  export function useChatSession(client) {
7
- const connectionStatus = useChatStore((state) => state.connectionStatus);
8
- const sessionId = useChatStore((state) => state.sessionId);
9
- const setConnectionStatus = useChatStore(
10
- (state) => state.setConnectionStatus,
11
- );
12
- const setSessionId = useChatStore((state) => state.setSessionId);
13
- const setError = useChatStore((state) => state.setError);
14
- const clearMessages = useChatStore((state) => state.clearMessages);
15
- /**
16
- * Connect to the agent
17
- */
18
- const connect = useCallback(async () => {
19
- if (!client) {
20
- setError("No client available");
21
- return;
22
- }
23
- try {
24
- setConnectionStatus("connecting");
25
- setError(null);
26
- await client.connect();
27
- setConnectionStatus("connected");
28
- } catch (error) {
29
- console.log(error);
30
- const message = error instanceof Error ? error.message : String(error);
31
- setError(message);
32
- setConnectionStatus("error");
33
- }
34
- }, [client, setConnectionStatus, setError]);
35
- /**
36
- * Start a new session
37
- */
38
- const startSession = useCallback(async () => {
39
- if (!client) {
40
- setError("No client available");
41
- return;
42
- }
43
- try {
44
- const id = await client.startSession();
45
- setSessionId(id);
46
- clearMessages();
47
- } catch (error) {
48
- const message = error instanceof Error ? error.message : String(error);
49
- setError(message);
50
- }
51
- }, [client, setSessionId, setError, clearMessages]);
52
- /**
53
- * Disconnect from the agent
54
- */
55
- const disconnect = useCallback(async () => {
56
- if (!client) return;
57
- try {
58
- await client.disconnect();
59
- setConnectionStatus("disconnected");
60
- setSessionId(null);
61
- } catch (error) {
62
- const message = error instanceof Error ? error.message : String(error);
63
- setError(message);
64
- }
65
- }, [client, setConnectionStatus, setSessionId, setError]);
66
- // Auto-connect on mount if client is available
67
- useEffect(() => {
68
- if (client && connectionStatus === "disconnected") {
69
- connect();
70
- }
71
- }, [client, connectionStatus, connect]);
72
- // Auto-start session after connecting
73
- useEffect(() => {
74
- if (connectionStatus === "connected" && !sessionId) {
75
- startSession();
76
- }
77
- }, [connectionStatus, sessionId, startSession]);
78
- return {
79
- connectionStatus,
80
- sessionId,
81
- connect,
82
- startSession,
83
- disconnect,
84
- };
7
+ const connectionStatus = useChatStore((state) => state.connectionStatus);
8
+ const sessionId = useChatStore((state) => state.sessionId);
9
+ const setConnectionStatus = useChatStore((state) => state.setConnectionStatus);
10
+ const setSessionId = useChatStore((state) => state.setSessionId);
11
+ const setError = useChatStore((state) => state.setError);
12
+ const clearMessages = useChatStore((state) => state.clearMessages);
13
+ /**
14
+ * Connect to the agent
15
+ */
16
+ const connect = useCallback(async () => {
17
+ if (!client) {
18
+ setError("No client available");
19
+ return;
20
+ }
21
+ try {
22
+ setConnectionStatus("connecting");
23
+ setError(null);
24
+ await client.connect();
25
+ setConnectionStatus("connected");
26
+ }
27
+ catch (error) {
28
+ console.log(error);
29
+ const message = error instanceof Error ? error.message : String(error);
30
+ setError(message);
31
+ setConnectionStatus("error");
32
+ }
33
+ }, [client, setConnectionStatus, setError]);
34
+ /**
35
+ * Start a new session
36
+ */
37
+ const startSession = useCallback(async () => {
38
+ if (!client) {
39
+ setError("No client available");
40
+ return;
41
+ }
42
+ try {
43
+ const id = await client.startSession();
44
+ setSessionId(id);
45
+ clearMessages();
46
+ }
47
+ catch (error) {
48
+ const message = error instanceof Error ? error.message : String(error);
49
+ setError(message);
50
+ }
51
+ }, [client, setSessionId, setError, clearMessages]);
52
+ /**
53
+ * Disconnect from the agent
54
+ */
55
+ const disconnect = useCallback(async () => {
56
+ if (!client)
57
+ return;
58
+ try {
59
+ await client.disconnect();
60
+ setConnectionStatus("disconnected");
61
+ setSessionId(null);
62
+ }
63
+ catch (error) {
64
+ const message = error instanceof Error ? error.message : String(error);
65
+ setError(message);
66
+ }
67
+ }, [client, setConnectionStatus, setSessionId, setError]);
68
+ // Auto-connect on mount if client is available
69
+ useEffect(() => {
70
+ if (client && connectionStatus === "disconnected") {
71
+ connect();
72
+ }
73
+ }, [client, connectionStatus, connect]);
74
+ // Auto-start session after connecting
75
+ useEffect(() => {
76
+ if (connectionStatus === "connected" && !sessionId) {
77
+ startSession();
78
+ }
79
+ }, [connectionStatus, sessionId, startSession]);
80
+ return {
81
+ connectionStatus,
82
+ sessionId,
83
+ connect,
84
+ startSession,
85
+ disconnect,
86
+ };
85
87
  }
@@ -1,27 +1,11 @@
1
1
  import { type VariantProps } from "class-variance-authority";
2
2
  import * as React from "react";
3
- declare const buttonVariants: (
4
- props?:
5
- | ({
6
- variant?:
7
- | "default"
8
- | "destructive"
9
- | "outline"
10
- | "secondary"
11
- | "ghost"
12
- | "link"
13
- | null
14
- | undefined;
15
- size?: "default" | "sm" | "lg" | "icon" | null | undefined;
16
- } & import("class-variance-authority/types").ClassProp)
17
- | undefined,
18
- ) => string;
19
- export interface ButtonProps
20
- extends React.ButtonHTMLAttributes<HTMLButtonElement>,
21
- VariantProps<typeof buttonVariants> {
22
- asChild?: boolean;
3
+ declare const buttonVariants: (props?: ({
4
+ variant?: "default" | "link" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
5
+ size?: "default" | "sm" | "lg" | "icon" | null | undefined;
6
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
+ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
8
+ asChild?: boolean;
23
9
  }
24
- declare const Button: React.ForwardRefExoticComponent<
25
- ButtonProps & React.RefAttributes<HTMLButtonElement>
26
- >;
10
+ declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
27
11
  export { Button, buttonVariants };