@townco/ui 0.1.45 → 0.1.46
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 +6 -0
- package/dist/core/hooks/use-chat-messages.js +19 -8
- package/dist/core/hooks/use-chat-session.js +19 -3
- package/dist/core/hooks/use-tool-calls.d.ts +6 -0
- package/dist/core/schemas/chat.d.ts +18 -0
- package/dist/core/schemas/tool-call.d.ts +9 -0
- package/dist/core/schemas/tool-call.js +9 -0
- package/dist/core/store/chat-store.d.ts +14 -2
- package/dist/core/store/chat-store.js +3 -3
- package/dist/gui/components/ChatView.js +29 -12
- package/dist/gui/components/ContextUsageButton.d.ts +11 -3
- package/dist/gui/components/ContextUsageButton.js +22 -3
- package/dist/gui/components/MessageContent.d.ts +5 -0
- package/dist/gui/components/MessageContent.js +2 -55
- package/dist/gui/components/ToolCall.js +1 -1
- package/dist/sdk/client/acp-client.d.ts +12 -0
- package/dist/sdk/client/acp-client.js +19 -0
- package/dist/sdk/schemas/message.d.ts +13 -2
- package/dist/sdk/schemas/message.js +15 -0
- package/dist/sdk/schemas/session.d.ts +19 -6
- package/dist/sdk/schemas/session.js +1 -0
- package/dist/sdk/transports/http.d.ts +8 -0
- package/dist/sdk/transports/http.js +71 -8
- package/dist/sdk/transports/stdio.d.ts +8 -0
- package/dist/sdk/transports/stdio.js +28 -0
- package/dist/sdk/transports/types.d.ts +12 -0
- package/dist/tui/components/ToolCall.js +1 -1
- package/package.json +3 -3
- package/src/styles/global.css +15 -0
|
@@ -52,6 +52,12 @@ export declare function useChatMessages(client: AcpClient | null, startSession:
|
|
|
52
52
|
outputTokens?: number | undefined;
|
|
53
53
|
totalTokens?: number | undefined;
|
|
54
54
|
} | undefined;
|
|
55
|
+
_meta?: {
|
|
56
|
+
truncationWarning?: string | undefined;
|
|
57
|
+
compactionAction?: "compacted" | "truncated" | undefined;
|
|
58
|
+
originalTokens?: number | undefined;
|
|
59
|
+
finalTokens?: number | undefined;
|
|
60
|
+
} | undefined;
|
|
55
61
|
}[] | undefined;
|
|
56
62
|
tokenUsage?: {
|
|
57
63
|
inputTokens?: number | undefined;
|
|
@@ -14,7 +14,7 @@ export function useChatMessages(client, startSession) {
|
|
|
14
14
|
const addMessage = useChatStore((state) => state.addMessage);
|
|
15
15
|
const updateMessage = useChatStore((state) => state.updateMessage);
|
|
16
16
|
const setError = useChatStore((state) => state.setError);
|
|
17
|
-
const
|
|
17
|
+
const setLatestContextSize = useChatStore((state) => state.setLatestContextSize);
|
|
18
18
|
/**
|
|
19
19
|
* Send a message to the agent
|
|
20
20
|
*/
|
|
@@ -79,14 +79,24 @@ export function useChatMessages(client, startSession) {
|
|
|
79
79
|
let accumulatedContent = "";
|
|
80
80
|
let streamCompleted = false;
|
|
81
81
|
for await (const chunk of messageStream) {
|
|
82
|
-
// Update context
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
82
|
+
// Update context size if provided (check both _meta.context_size and direct context_size)
|
|
83
|
+
const chunkMeta = chunk._meta;
|
|
84
|
+
const contextSizeData = chunkMeta?.context_size || chunk.context_size;
|
|
85
|
+
if (contextSizeData != null) {
|
|
86
|
+
const contextSize = contextSizeData;
|
|
87
|
+
logger.info("✅ Received context_size from backend", {
|
|
88
|
+
context_size: contextSize,
|
|
89
|
+
totalEstimated: contextSize.totalEstimated,
|
|
90
|
+
source: chunkMeta?.context_size ? "_meta" : "direct",
|
|
91
|
+
});
|
|
92
|
+
setLatestContextSize(contextSize);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
logger.debug("Chunk does not have context_size", {
|
|
96
|
+
hasContextSize: "context_size" in chunk,
|
|
97
|
+
hasMeta: "_meta" in chunk,
|
|
98
|
+
metaKeys: chunkMeta ? Object.keys(chunkMeta) : null,
|
|
88
99
|
});
|
|
89
|
-
setLatestContextInputTokens(tokens);
|
|
90
100
|
}
|
|
91
101
|
if (chunk.tokenUsage) {
|
|
92
102
|
logger.debug("Received tokenUsage from backend", {
|
|
@@ -150,6 +160,7 @@ export function useChatMessages(client, startSession) {
|
|
|
150
160
|
setIsStreaming,
|
|
151
161
|
setStreamingStartTime,
|
|
152
162
|
setError,
|
|
163
|
+
setLatestContextSize,
|
|
153
164
|
]);
|
|
154
165
|
return {
|
|
155
166
|
messages,
|
|
@@ -14,11 +14,24 @@ export function useChatSession(client, initialSessionId) {
|
|
|
14
14
|
const clearMessages = useChatStore((state) => state.clearMessages);
|
|
15
15
|
const resetTokens = useChatStore((state) => state.resetTokens);
|
|
16
16
|
const addMessage = useChatStore((state) => state.addMessage);
|
|
17
|
+
const setLatestContextSize = useChatStore((state) => state.setLatestContextSize);
|
|
17
18
|
// Subscribe to session updates to handle replayed messages
|
|
18
19
|
useEffect(() => {
|
|
19
20
|
if (!client)
|
|
20
21
|
return;
|
|
21
22
|
const unsubscribe = client.onSessionUpdate((update) => {
|
|
23
|
+
// Extract context size from update metadata if available
|
|
24
|
+
const updateMeta = update._meta;
|
|
25
|
+
const contextSizeData = updateMeta?.context_size || update.context_size;
|
|
26
|
+
if (contextSizeData != null) {
|
|
27
|
+
const contextSize = contextSizeData;
|
|
28
|
+
logger.info("✅ Received context_size from session update", {
|
|
29
|
+
context_size: contextSize,
|
|
30
|
+
totalEstimated: contextSize.totalEstimated,
|
|
31
|
+
isReplay: updateMeta?.isReplay,
|
|
32
|
+
});
|
|
33
|
+
setLatestContextSize(contextSize);
|
|
34
|
+
}
|
|
22
35
|
// Handle replayed messages during session loading
|
|
23
36
|
if (update.message) {
|
|
24
37
|
logger.debug("Session update with message", {
|
|
@@ -64,7 +77,7 @@ export function useChatSession(client, initialSessionId) {
|
|
|
64
77
|
}
|
|
65
78
|
});
|
|
66
79
|
return unsubscribe;
|
|
67
|
-
}, [client, addMessage]);
|
|
80
|
+
}, [client, addMessage, setLatestContextSize]);
|
|
68
81
|
/**
|
|
69
82
|
* Connect to the agent (without creating a session)
|
|
70
83
|
*/
|
|
@@ -99,12 +112,15 @@ export function useChatSession(client, initialSessionId) {
|
|
|
99
112
|
try {
|
|
100
113
|
setConnectionStatus("connecting");
|
|
101
114
|
setError(null);
|
|
115
|
+
// Clear existing UI state before loading
|
|
116
|
+
clearMessages();
|
|
117
|
+
resetTokens(); // Clears latestContextSize temporarily
|
|
102
118
|
// Try to load session (this will also connect and replay messages)
|
|
103
119
|
const id = await client.loadSession(sessionIdToLoad);
|
|
104
120
|
setSessionId(id);
|
|
105
121
|
setConnectionStatus("connected");
|
|
106
|
-
//
|
|
107
|
-
|
|
122
|
+
// Messages and context size will be restored from backend during replay
|
|
123
|
+
// Backend sends context_size after replay completes (see adapter.ts:369-401)
|
|
108
124
|
logger.info("Session loaded successfully", { sessionId: id });
|
|
109
125
|
}
|
|
110
126
|
catch (error) {
|
|
@@ -50,6 +50,12 @@ export declare function useToolCalls(client: AcpClient | null): {
|
|
|
50
50
|
outputTokens?: number | undefined;
|
|
51
51
|
totalTokens?: number | undefined;
|
|
52
52
|
} | undefined;
|
|
53
|
+
_meta?: {
|
|
54
|
+
truncationWarning?: string | undefined;
|
|
55
|
+
compactionAction?: "compacted" | "truncated" | undefined;
|
|
56
|
+
originalTokens?: number | undefined;
|
|
57
|
+
finalTokens?: number | undefined;
|
|
58
|
+
} | undefined;
|
|
53
59
|
}[]>;
|
|
54
60
|
getToolCallsForSession: (sessionId: string) => ToolCall[];
|
|
55
61
|
};
|
|
@@ -74,6 +74,15 @@ export declare const DisplayMessage: z.ZodObject<{
|
|
|
74
74
|
outputTokens: z.ZodOptional<z.ZodNumber>;
|
|
75
75
|
totalTokens: z.ZodOptional<z.ZodNumber>;
|
|
76
76
|
}, z.core.$strip>>;
|
|
77
|
+
_meta: z.ZodOptional<z.ZodObject<{
|
|
78
|
+
truncationWarning: z.ZodOptional<z.ZodString>;
|
|
79
|
+
compactionAction: z.ZodOptional<z.ZodEnum<{
|
|
80
|
+
compacted: "compacted";
|
|
81
|
+
truncated: "truncated";
|
|
82
|
+
}>>;
|
|
83
|
+
originalTokens: z.ZodOptional<z.ZodNumber>;
|
|
84
|
+
finalTokens: z.ZodOptional<z.ZodNumber>;
|
|
85
|
+
}, z.core.$strip>>;
|
|
77
86
|
}, z.core.$strip>>>;
|
|
78
87
|
tokenUsage: z.ZodOptional<z.ZodObject<{
|
|
79
88
|
inputTokens: z.ZodOptional<z.ZodNumber>;
|
|
@@ -172,6 +181,15 @@ export declare const ChatSessionState: z.ZodObject<{
|
|
|
172
181
|
outputTokens: z.ZodOptional<z.ZodNumber>;
|
|
173
182
|
totalTokens: z.ZodOptional<z.ZodNumber>;
|
|
174
183
|
}, z.core.$strip>>;
|
|
184
|
+
_meta: z.ZodOptional<z.ZodObject<{
|
|
185
|
+
truncationWarning: z.ZodOptional<z.ZodString>;
|
|
186
|
+
compactionAction: z.ZodOptional<z.ZodEnum<{
|
|
187
|
+
compacted: "compacted";
|
|
188
|
+
truncated: "truncated";
|
|
189
|
+
}>>;
|
|
190
|
+
originalTokens: z.ZodOptional<z.ZodNumber>;
|
|
191
|
+
finalTokens: z.ZodOptional<z.ZodNumber>;
|
|
192
|
+
}, z.core.$strip>>;
|
|
175
193
|
}, z.core.$strip>>>;
|
|
176
194
|
tokenUsage: z.ZodOptional<z.ZodObject<{
|
|
177
195
|
inputTokens: z.ZodOptional<z.ZodNumber>;
|
|
@@ -125,6 +125,15 @@ export declare const ToolCallSchema: z.ZodObject<{
|
|
|
125
125
|
outputTokens: z.ZodOptional<z.ZodNumber>;
|
|
126
126
|
totalTokens: z.ZodOptional<z.ZodNumber>;
|
|
127
127
|
}, z.core.$strip>>;
|
|
128
|
+
_meta: z.ZodOptional<z.ZodObject<{
|
|
129
|
+
truncationWarning: z.ZodOptional<z.ZodString>;
|
|
130
|
+
compactionAction: z.ZodOptional<z.ZodEnum<{
|
|
131
|
+
compacted: "compacted";
|
|
132
|
+
truncated: "truncated";
|
|
133
|
+
}>>;
|
|
134
|
+
originalTokens: z.ZodOptional<z.ZodNumber>;
|
|
135
|
+
finalTokens: z.ZodOptional<z.ZodNumber>;
|
|
136
|
+
}, z.core.$strip>>;
|
|
128
137
|
}, z.core.$strip>;
|
|
129
138
|
export type ToolCall = z.infer<typeof ToolCallSchema>;
|
|
130
139
|
/**
|
|
@@ -101,6 +101,15 @@ export const ToolCallSchema = z.object({
|
|
|
101
101
|
completedAt: z.number().optional(),
|
|
102
102
|
/** Token usage metadata for this tool call */
|
|
103
103
|
tokenUsage: TokenUsageSchema.optional(),
|
|
104
|
+
/** Internal metadata (e.g., truncation warnings) */
|
|
105
|
+
_meta: z
|
|
106
|
+
.object({
|
|
107
|
+
truncationWarning: z.string().optional(),
|
|
108
|
+
compactionAction: z.enum(["compacted", "truncated"]).optional(),
|
|
109
|
+
originalTokens: z.number().optional(),
|
|
110
|
+
finalTokens: z.number().optional(),
|
|
111
|
+
})
|
|
112
|
+
.optional(),
|
|
104
113
|
});
|
|
105
114
|
/**
|
|
106
115
|
* Partial update for an existing tool call
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { type LogEntry } from "@townco/core";
|
|
2
2
|
import type { ConnectionStatus, DisplayMessage, InputState } from "../schemas/index.js";
|
|
3
3
|
import type { ToolCall, ToolCallUpdate } from "../schemas/tool-call.js";
|
|
4
|
+
/**
|
|
5
|
+
* Context size breakdown from agent
|
|
6
|
+
*/
|
|
7
|
+
export interface ContextSize {
|
|
8
|
+
systemPromptTokens: number;
|
|
9
|
+
userMessagesTokens: number;
|
|
10
|
+
assistantMessagesTokens: number;
|
|
11
|
+
toolInputTokens: number;
|
|
12
|
+
toolResultsTokens: number;
|
|
13
|
+
totalEstimated: number;
|
|
14
|
+
llmReportedInputTokens?: number;
|
|
15
|
+
}
|
|
4
16
|
/**
|
|
5
17
|
* Chat store state
|
|
6
18
|
*/
|
|
@@ -22,7 +34,7 @@ export interface ChatStore {
|
|
|
22
34
|
outputTokens: number;
|
|
23
35
|
totalTokens: number;
|
|
24
36
|
};
|
|
25
|
-
|
|
37
|
+
latestContextSize: ContextSize | null;
|
|
26
38
|
currentModel: string | null;
|
|
27
39
|
tokenDisplayMode: "context" | "input" | "output";
|
|
28
40
|
logs: LogEntry[];
|
|
@@ -50,7 +62,7 @@ export interface ChatStore {
|
|
|
50
62
|
outputTokens?: number;
|
|
51
63
|
totalTokens?: number;
|
|
52
64
|
}) => void;
|
|
53
|
-
|
|
65
|
+
setLatestContextSize: (contextSize: ContextSize | null) => void;
|
|
54
66
|
setCurrentModel: (model: string) => void;
|
|
55
67
|
resetTokens: () => void;
|
|
56
68
|
cycleTokenDisplayMode: () => void;
|
|
@@ -24,7 +24,7 @@ export const useChatStore = create((set) => ({
|
|
|
24
24
|
outputTokens: 0,
|
|
25
25
|
totalTokens: 0,
|
|
26
26
|
},
|
|
27
|
-
|
|
27
|
+
latestContextSize: null,
|
|
28
28
|
currentModel: "claude-sonnet-4-5-20250929", // Default model, TODO: get from server
|
|
29
29
|
tokenDisplayMode: "context", // Default to showing context (both billed and current)
|
|
30
30
|
logs: [],
|
|
@@ -279,7 +279,7 @@ export const useChatStore = create((set) => ({
|
|
|
279
279
|
(state.currentContext.outputTokens + (tokenUsage.outputTokens ?? 0)),
|
|
280
280
|
},
|
|
281
281
|
})),
|
|
282
|
-
|
|
282
|
+
setLatestContextSize: (contextSize) => set({ latestContextSize: contextSize }),
|
|
283
283
|
setCurrentModel: (model) => set({ currentModel: model }),
|
|
284
284
|
resetTokens: () => set({
|
|
285
285
|
totalBilled: {
|
|
@@ -292,7 +292,7 @@ export const useChatStore = create((set) => ({
|
|
|
292
292
|
outputTokens: 0,
|
|
293
293
|
totalTokens: 0,
|
|
294
294
|
},
|
|
295
|
-
|
|
295
|
+
latestContextSize: null,
|
|
296
296
|
}),
|
|
297
297
|
cycleTokenDisplayMode: () => set((state) => {
|
|
298
298
|
const modes = [
|
|
@@ -42,6 +42,11 @@ export function ChatView({ client, initialSessionId, error: initError, }) {
|
|
|
42
42
|
const error = useChatStore((state) => state.error);
|
|
43
43
|
const currentModel = useChatStore((state) => state.currentModel);
|
|
44
44
|
const [agentName, setAgentName] = useState("Agent");
|
|
45
|
+
const [agentDescription, setAgentDescription] = useState("This agent can help you with your tasks. Start a conversation by typing a message below.");
|
|
46
|
+
const [suggestedPrompts, setSuggestedPrompts] = useState([
|
|
47
|
+
"Search the web for the latest news on top tech company earnings, produce a summary for each company, and then a macro trend analysis of the tech industry. Use your todo list",
|
|
48
|
+
"What can you help me with?",
|
|
49
|
+
]);
|
|
45
50
|
const [isLargeScreen, setIsLargeScreen] = useState(typeof window !== "undefined" ? window.innerWidth >= 1024 : true);
|
|
46
51
|
const [placeholder, setPlaceholder] = useState("Type a message or / for commands...");
|
|
47
52
|
// Log connection status changes
|
|
@@ -51,15 +56,32 @@ export function ChatView({ client, initialSessionId, error: initError, }) {
|
|
|
51
56
|
logger.error("Connection error occurred", { error });
|
|
52
57
|
}
|
|
53
58
|
}, [connectionStatus, error]);
|
|
54
|
-
// Get agent name from session metadata
|
|
59
|
+
// Get agent name from session metadata or client info
|
|
55
60
|
useEffect(() => {
|
|
56
|
-
if (client
|
|
61
|
+
if (client) {
|
|
62
|
+
// Try to get from current session first
|
|
57
63
|
const session = client.getCurrentSession();
|
|
58
64
|
if (session?.metadata?.agentName) {
|
|
59
65
|
setAgentName(session.metadata.agentName);
|
|
66
|
+
// We don't currently have description in session metadata, but we could add it
|
|
67
|
+
}
|
|
68
|
+
// Fallback to client info (if connected)
|
|
69
|
+
const agentInfo = client.getAgentInfo();
|
|
70
|
+
// Prefer displayName (human-readable) over name (programmatic)
|
|
71
|
+
if (agentInfo.displayName) {
|
|
72
|
+
setAgentName(agentInfo.displayName);
|
|
73
|
+
}
|
|
74
|
+
else if (agentInfo.name) {
|
|
75
|
+
setAgentName(agentInfo.name);
|
|
76
|
+
}
|
|
77
|
+
if (agentInfo.description) {
|
|
78
|
+
setAgentDescription(agentInfo.description);
|
|
79
|
+
}
|
|
80
|
+
if (agentInfo.suggestedPrompts && agentInfo.suggestedPrompts.length > 0) {
|
|
81
|
+
setSuggestedPrompts(agentInfo.suggestedPrompts);
|
|
60
82
|
}
|
|
61
83
|
}
|
|
62
|
-
}, [client, sessionId]);
|
|
84
|
+
}, [client, sessionId, connectionStatus]);
|
|
63
85
|
// Monitor screen size changes and update isLargeScreen state
|
|
64
86
|
useEffect(() => {
|
|
65
87
|
const mediaQuery = window.matchMedia("(min-width: 1024px)");
|
|
@@ -123,8 +145,8 @@ export function ChatView({ client, initialSessionId, error: initError, }) {
|
|
|
123
145
|
favicon: "https://www.google.com/s2/favicons?domain=theverge.com&sz=32",
|
|
124
146
|
},
|
|
125
147
|
];
|
|
126
|
-
// Get the latest context
|
|
127
|
-
const
|
|
148
|
+
// Get the latest context size from the session context
|
|
149
|
+
const latestContextSize = useChatStore((state) => state.latestContextSize);
|
|
128
150
|
// Command menu items for chat input
|
|
129
151
|
const commandMenuItems = [
|
|
130
152
|
{
|
|
@@ -168,12 +190,7 @@ export function ChatView({ client, initialSessionId, error: initError, }) {
|
|
|
168
190
|
},
|
|
169
191
|
},
|
|
170
192
|
];
|
|
171
|
-
return (_jsxs(ChatLayout.Root, { defaultPanelSize: "hidden", defaultActiveTab: "todo", children: [_jsxs(ChatLayout.Main, { children: [_jsx(AppChatHeader, { agentName: agentName, todos: todos, sources: sources, showHeader: messages.length > 0 }), connectionStatus === "error" && error && (_jsx("div", { className: "border-b border-destructive/20 bg-destructive/10 px-6 py-4", children: _jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "flex-1", children: [_jsx("h3", { className: "mb-1 text-paragraph-sm font-semibold text-destructive", children: "Connection Error" }), _jsx("p", { className: "whitespace-pre-line text-paragraph-sm text-foreground", children: error })] }), _jsx("button", { type: "button", onClick: connect, className: "rounded-lg bg-destructive px-4 py-2 text-paragraph-sm font-medium text-destructive-foreground transition-colors hover:bg-destructive-hover", children: "Retry" })] }) })), _jsxs(ChatLayout.Body, { children: [_jsx(ChatLayout.Messages, { children: messages.length === 0 ? (_jsx(OpenFilesButton, { children: ({ openFiles }) => (_jsx("div", { className: "flex flex-1 items-center px-4", children: _jsx(ChatEmptyState, { title: agentName, description:
|
|
172
|
-
"Search the web for the latest news on top tech company earnings, produce a summary for each company, and then a macro trend analysis of the tech industry. Use your todo list",
|
|
173
|
-
"Explain how this works",
|
|
174
|
-
"Create a new feature",
|
|
175
|
-
"Review my changes",
|
|
176
|
-
], onPromptClick: (prompt) => {
|
|
193
|
+
return (_jsxs(ChatLayout.Root, { defaultPanelSize: "hidden", defaultActiveTab: "todo", children: [_jsxs(ChatLayout.Main, { children: [_jsx(AppChatHeader, { agentName: agentName, todos: todos, sources: sources, showHeader: messages.length > 0 }), connectionStatus === "error" && error && (_jsx("div", { className: "border-b border-destructive/20 bg-destructive/10 px-6 py-4", children: _jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "flex-1", children: [_jsx("h3", { className: "mb-1 text-paragraph-sm font-semibold text-destructive", children: "Connection Error" }), _jsx("p", { className: "whitespace-pre-line text-paragraph-sm text-foreground", children: error })] }), _jsx("button", { type: "button", onClick: connect, className: "rounded-lg bg-destructive px-4 py-2 text-paragraph-sm font-medium text-destructive-foreground transition-colors hover:bg-destructive-hover", children: "Retry" })] }) })), _jsxs(ChatLayout.Body, { children: [_jsx(ChatLayout.Messages, { children: messages.length === 0 ? (_jsx(OpenFilesButton, { children: ({ openFiles }) => (_jsx("div", { className: "flex flex-1 items-center px-4", children: _jsx(ChatEmptyState, { title: agentName, description: agentDescription, suggestedPrompts: suggestedPrompts, onPromptClick: (prompt) => {
|
|
177
194
|
sendMessage(prompt);
|
|
178
195
|
setPlaceholder("Type a message or / for commands...");
|
|
179
196
|
logger.info("Prompt clicked", { prompt });
|
|
@@ -196,5 +213,5 @@ export function ChatView({ client, initialSessionId, error: initError, }) {
|
|
|
196
213
|
previousMessage?.role === "assistant" ? "mt-2" : "mt-6";
|
|
197
214
|
}
|
|
198
215
|
return (_jsx(Message, { message: message, className: spacingClass, isLastMessage: index === messages.length - 1, children: _jsx(MessageContent, { message: message, thinkingDisplayStyle: "collapsible" }) }, message.id));
|
|
199
|
-
}) })) }), _jsx(ChatLayout.Footer, { children: _jsxs(ChatInputRoot, { client: client, startSession: startSession, children: [_jsx(ChatInputCommandMenu, { commands: commandMenuItems }), _jsx(ChatInputField, { placeholder: placeholder, autoFocus: true }), _jsxs(ChatInputToolbar, { children: [_jsxs("div", { className: "flex items-baseline gap-1", children: [_jsx(ChatInputActions, {}), _jsx(ChatInputAttachment, {}),
|
|
216
|
+
}) })) }), _jsx(ChatLayout.Footer, { children: _jsxs(ChatInputRoot, { client: client, startSession: startSession, children: [_jsx(ChatInputCommandMenu, { commands: commandMenuItems }), _jsx(ChatInputField, { placeholder: placeholder, autoFocus: true }), _jsxs(ChatInputToolbar, { children: [_jsxs("div", { className: "flex items-baseline gap-1", children: [_jsx(ChatInputActions, {}), _jsx(ChatInputAttachment, {}), latestContextSize != null && (_jsx(ContextUsageButton, { contextSize: latestContextSize, modelContextWindow: currentModel?.includes("claude") ? 200000 : 128000 }))] }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx(ChatInputVoiceInput, {}), _jsx(ChatInputSubmit, { children: _jsx(ArrowUp, { className: "size-4" }) })] })] })] }) })] })] }), isLargeScreen && (_jsx(ChatLayout.Aside, { breakpoint: "lg", children: _jsx(AsideTabs, {}) }))] }));
|
|
200
217
|
}
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
export interface ContextSize {
|
|
3
|
+
systemPromptTokens: number;
|
|
4
|
+
userMessagesTokens: number;
|
|
5
|
+
assistantMessagesTokens: number;
|
|
6
|
+
toolInputTokens: number;
|
|
7
|
+
toolResultsTokens: number;
|
|
8
|
+
totalEstimated: number;
|
|
9
|
+
llmReportedInputTokens?: number;
|
|
10
|
+
}
|
|
2
11
|
export interface ContextUsageButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
formattedPercentage: string;
|
|
12
|
+
contextSize: ContextSize;
|
|
13
|
+
modelContextWindow: number;
|
|
6
14
|
}
|
|
7
15
|
export declare const ContextUsageButton: React.ForwardRefExoticComponent<ContextUsageButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
@@ -3,9 +3,28 @@ import * as React from "react";
|
|
|
3
3
|
import { cn } from "../lib/utils.js";
|
|
4
4
|
import { Button } from "./Button.js";
|
|
5
5
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "./Tooltip.js";
|
|
6
|
-
export const ContextUsageButton = React.forwardRef(({
|
|
7
|
-
//
|
|
6
|
+
export const ContextUsageButton = React.forwardRef(({ contextSize, modelContextWindow, className, ...props }, ref) => {
|
|
7
|
+
// Use max of estimated and LLM-reported tokens (LLM reported as fallback if higher)
|
|
8
|
+
const actualTokens = Math.max(contextSize.totalEstimated, contextSize.llmReportedInputTokens ?? 0);
|
|
9
|
+
const percentage = (actualTokens / modelContextWindow) * 100;
|
|
10
|
+
const formattedPercentage = `${percentage.toFixed(1)}%`;
|
|
11
|
+
// Clamp percentage between 0 and 100 for display
|
|
8
12
|
const clampedPercentage = Math.min(100, Math.max(0, percentage));
|
|
13
|
+
// Determine color based on percentage thresholds
|
|
14
|
+
const getColorClass = (pct) => {
|
|
15
|
+
if (pct < 50)
|
|
16
|
+
return "text-foreground"; // Normal text color
|
|
17
|
+
if (pct < 75)
|
|
18
|
+
return "text-yellow-500"; // Yellow
|
|
19
|
+
return "text-red-500"; // Red
|
|
20
|
+
};
|
|
21
|
+
const colorClass = getColorClass(percentage);
|
|
22
|
+
// Calculate percentage contribution for each element
|
|
23
|
+
const calculatePercentage = (tokens) => {
|
|
24
|
+
if (actualTokens === 0)
|
|
25
|
+
return "0.0%";
|
|
26
|
+
return `${((tokens / actualTokens) * 100).toFixed(1)}%`;
|
|
27
|
+
};
|
|
9
28
|
// SVG parameters
|
|
10
29
|
const size = 16;
|
|
11
30
|
const strokeWidth = 2;
|
|
@@ -13,6 +32,6 @@ export const ContextUsageButton = React.forwardRef(({ percentage, tokens, format
|
|
|
13
32
|
const center = size / 2;
|
|
14
33
|
const circumference = 2 * Math.PI * radius;
|
|
15
34
|
const offset = circumference - (clampedPercentage / 100) * circumference;
|
|
16
|
-
return (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { ref: ref, type: "button", variant: "ghost", size: "icon", className: cn("rounded-full cursor-default", className), ...props, children: _jsxs("svg", { width: size, height: size, viewBox: `0 0 ${size} ${size}`, className: "transform -rotate-90", children: [_jsx("circle", { cx: center, cy: center, r: radius, stroke: "currentColor", strokeWidth: strokeWidth, fill: "transparent", className: "opacity-20" }), _jsx("circle", { cx: center, cy: center, r: radius, stroke: "currentColor", strokeWidth: strokeWidth, fill: "transparent", strokeDasharray: circumference, strokeDashoffset: offset, strokeLinecap: "round", className: "transition-all duration-300 ease-in-out" })] }) }) }), _jsx(TooltipContent, { side: "top", align: "center", children: _jsxs("
|
|
35
|
+
return (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { ref: ref, type: "button", variant: "ghost", size: "icon", className: cn("rounded-full cursor-default", colorClass, className), ...props, children: _jsxs("svg", { width: size, height: size, viewBox: `0 0 ${size} ${size}`, className: "transform -rotate-90", children: [_jsx("circle", { cx: center, cy: center, r: radius, stroke: "currentColor", strokeWidth: strokeWidth, fill: "transparent", className: "opacity-20" }), _jsx("circle", { cx: center, cy: center, r: radius, stroke: "currentColor", strokeWidth: strokeWidth, fill: "transparent", strokeDasharray: circumference, strokeDashoffset: offset, strokeLinecap: "round", className: "transition-all duration-300 ease-in-out" })] }) }) }), _jsx(TooltipContent, { side: "top", align: "center", className: "p-3", children: _jsxs("div", { className: "space-y-2 font-mono text-xs", children: [_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex justify-between gap-6", children: [_jsx("span", { className: "text-muted-foreground", children: "System Prompt:" }), _jsxs("div", { className: "flex gap-1 items-baseline", children: [_jsx("span", { children: contextSize.systemPromptTokens.toLocaleString() }), _jsxs("span", { className: "text-muted-foreground text-[10px]", children: ["(", calculatePercentage(contextSize.systemPromptTokens), ")"] })] })] }), _jsxs("div", { className: "flex justify-between gap-6", children: [_jsx("span", { className: "text-muted-foreground", children: "User Messages:" }), _jsxs("div", { className: "flex gap-1 items-baseline", children: [_jsx("span", { children: contextSize.userMessagesTokens.toLocaleString() }), _jsxs("span", { className: "text-muted-foreground text-[10px]", children: ["(", calculatePercentage(contextSize.userMessagesTokens), ")"] })] })] }), _jsxs("div", { className: "flex justify-between gap-6", children: [_jsx("span", { className: "text-muted-foreground", children: "Assistant Messages:" }), _jsxs("div", { className: "flex gap-1 items-baseline", children: [_jsx("span", { children: contextSize.assistantMessagesTokens.toLocaleString() }), _jsxs("span", { className: "text-muted-foreground text-[10px]", children: ["(", calculatePercentage(contextSize.assistantMessagesTokens), ")"] })] })] }), _jsxs("div", { className: "flex justify-between gap-6", children: [_jsx("span", { className: "text-muted-foreground", children: "Tool Inputs:" }), _jsxs("div", { className: "flex gap-1 items-baseline", children: [_jsx("span", { children: contextSize.toolInputTokens.toLocaleString() }), _jsxs("span", { className: "text-muted-foreground text-[10px]", children: ["(", calculatePercentage(contextSize.toolInputTokens), ")"] })] })] }), _jsxs("div", { className: "flex justify-between gap-6", children: [_jsx("span", { className: "text-muted-foreground", children: "Tool Results:" }), _jsxs("div", { className: "flex gap-1 items-baseline", children: [_jsx("span", { children: contextSize.toolResultsTokens.toLocaleString() }), _jsxs("span", { className: "text-muted-foreground text-[10px]", children: ["(", calculatePercentage(contextSize.toolResultsTokens), ")"] })] })] })] }), _jsx("div", { className: "border-t border-border pt-2", children: _jsxs("div", { className: cn("flex justify-end gap-2 font-semibold", colorClass), children: [_jsx("span", { children: actualTokens.toLocaleString() }), _jsxs("span", { children: ["(", formattedPercentage, ")"] })] }) })] }) })] }) }));
|
|
17
36
|
});
|
|
18
37
|
ContextUsageButton.displayName = "ContextUsageButton";
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { type VariantProps } from "class-variance-authority";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import type { DisplayMessage } from "./MessageList.js";
|
|
4
|
+
/**
|
|
5
|
+
* MessageContent component inspired by shadcn.io/ai
|
|
6
|
+
* Provides the content container with role-based styling
|
|
7
|
+
* Handles automatic rendering of thinking, waiting states, and content
|
|
8
|
+
*/
|
|
4
9
|
declare const messageContentVariants: (props?: ({
|
|
5
10
|
role?: "user" | "assistant" | "system" | null | undefined;
|
|
6
11
|
variant?: "default" | "outline" | "ghost" | null | undefined;
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cva } from "class-variance-authority";
|
|
3
|
-
import { Loader2Icon } from "lucide-react";
|
|
4
3
|
import * as React from "react";
|
|
5
4
|
import { useChatStore } from "../../core/store/chat-store.js";
|
|
6
|
-
import { formatTokenPercentage } from "../../core/utils/model-context.js";
|
|
7
5
|
import { cn } from "../lib/utils.js";
|
|
8
6
|
import { Reasoning } from "./Reasoning.js";
|
|
9
7
|
import { Response } from "./Response.js";
|
|
@@ -13,57 +11,6 @@ import { ToolCall } from "./ToolCall.js";
|
|
|
13
11
|
* Provides the content container with role-based styling
|
|
14
12
|
* Handles automatic rendering of thinking, waiting states, and content
|
|
15
13
|
*/
|
|
16
|
-
// Synonyms of "thinking" in multiple languages
|
|
17
|
-
const THINKING_WORDS = [
|
|
18
|
-
"Thinking",
|
|
19
|
-
"Pensando",
|
|
20
|
-
"Pensant",
|
|
21
|
-
"Denkend",
|
|
22
|
-
"Pensando",
|
|
23
|
-
"考えている",
|
|
24
|
-
"생각 중",
|
|
25
|
-
"思考中",
|
|
26
|
-
"Размышляя",
|
|
27
|
-
"Düşünüyor",
|
|
28
|
-
"Myślący",
|
|
29
|
-
"Tänkande",
|
|
30
|
-
"Pensando",
|
|
31
|
-
"Ajatellen",
|
|
32
|
-
"Σκεπτόμενος",
|
|
33
|
-
"חושב",
|
|
34
|
-
"सोच रहा है",
|
|
35
|
-
"Berpikir",
|
|
36
|
-
];
|
|
37
|
-
const DOT_ANIMATIONS = ["...", "·..", ".·.", "..·", ".·.", "·.."];
|
|
38
|
-
function WaitingElapsedTime({ startTime }) {
|
|
39
|
-
const [elapsed, setElapsed] = React.useState(0);
|
|
40
|
-
const [thinkingWord, setThinkingWord] = React.useState(() => THINKING_WORDS[Math.floor(Math.random() * THINKING_WORDS.length)]);
|
|
41
|
-
const [dotIndex, setDotIndex] = React.useState(0);
|
|
42
|
-
React.useEffect(() => {
|
|
43
|
-
const interval = setInterval(() => {
|
|
44
|
-
const now = Date.now();
|
|
45
|
-
const elapsedMs = now - startTime;
|
|
46
|
-
setElapsed(elapsedMs);
|
|
47
|
-
}, 100);
|
|
48
|
-
return () => clearInterval(interval);
|
|
49
|
-
}, [startTime]);
|
|
50
|
-
React.useEffect(() => {
|
|
51
|
-
const wordInterval = setInterval(() => {
|
|
52
|
-
const randomIndex = Math.floor(Math.random() * THINKING_WORDS.length);
|
|
53
|
-
setThinkingWord(THINKING_WORDS[randomIndex]);
|
|
54
|
-
}, 1500);
|
|
55
|
-
return () => clearInterval(wordInterval);
|
|
56
|
-
}, []);
|
|
57
|
-
React.useEffect(() => {
|
|
58
|
-
const dotInterval = setInterval(() => {
|
|
59
|
-
setDotIndex((prev) => (prev + 1) % DOT_ANIMATIONS.length);
|
|
60
|
-
}, 100);
|
|
61
|
-
return () => clearInterval(dotInterval);
|
|
62
|
-
}, []);
|
|
63
|
-
const seconds = (elapsed / 1000).toFixed(1);
|
|
64
|
-
const animatedDots = DOT_ANIMATIONS[dotIndex];
|
|
65
|
-
return (_jsxs("span", { className: "text-muted-foreground text-paragraph-sm", children: [thinkingWord, animatedDots, " ", seconds, "s"] }));
|
|
66
|
-
}
|
|
67
14
|
const messageContentVariants = cva("w-full rounded-2xl text-[var(--font-size)] font-[var(--font-family)] leading-relaxed break-words transition-colors", {
|
|
68
15
|
variants: {
|
|
69
16
|
role: {
|
|
@@ -100,7 +47,7 @@ export const MessageContent = React.forwardRef(({ role: roleProp, variant, isStr
|
|
|
100
47
|
const hasThinking = !!thinking;
|
|
101
48
|
// Check if waiting (streaming but no content yet)
|
|
102
49
|
const isWaiting = message.isStreaming && !message.content && message.role === "assistant";
|
|
103
|
-
content = (_jsxs(_Fragment, { children: [message.role === "assistant" && hasThinking && (_jsx(Reasoning, { content: thinking, isStreaming: message.isStreaming, mode: thinkingDisplayStyle, autoCollapse: true })), isWaiting && streamingStartTime && (
|
|
50
|
+
content = (_jsxs(_Fragment, { children: [message.role === "assistant" && hasThinking && (_jsx(Reasoning, { content: thinking, isStreaming: message.isStreaming, mode: thinkingDisplayStyle, autoCollapse: true })), isWaiting && streamingStartTime && (_jsx("div", { className: "flex items-center gap-2", children: _jsx("div", { className: "size-2.5 rounded-full bg-foreground animate-pulse-scale" }) })), message.role === "assistant" ? ((() => {
|
|
104
51
|
// Sort tool calls by content position
|
|
105
52
|
const sortedToolCalls = (message.toolCalls || [])
|
|
106
53
|
.slice()
|
|
@@ -137,5 +137,5 @@ export function ToolCall({ toolCall }) {
|
|
|
137
137
|
return (_jsxs("div", { className: "bg-neutral-900 text-neutral-100 p-2 rounded text-[11px] font-mono", children: ["Terminal: ", block.terminalId] }, getBlockKey()));
|
|
138
138
|
}
|
|
139
139
|
return null;
|
|
140
|
-
}), toolCall.error && (_jsxs("div", { className: "text-destructive font-mono text-[11px] mt-2", children: ["Error: ", toolCall.error] }))] })] })) : null, (toolCall.tokenUsage || toolCall.startedAt) && (_jsxs("div", { className: "p-2 bg-muted/50 border-t border-border flex flex-wrap gap-4 text-[10px] text-muted-foreground font-sans", children: [toolCall.tokenUsage && (_jsxs("div", { className: "flex gap-3", children: [toolCall.tokenUsage.inputTokens !== undefined && (_jsxs("div", { children: [_jsx("span", { className: "uppercase tracking-wide font-semibold mr-1", children: "Input:" }), toolCall.tokenUsage.inputTokens.toLocaleString()] })), toolCall.tokenUsage.outputTokens !== undefined && (_jsxs("div", { children: [_jsx("span", { className: "uppercase tracking-wide font-semibold mr-1", children: "Output:" }), toolCall.tokenUsage.outputTokens.toLocaleString()] })), toolCall.tokenUsage.totalTokens !== undefined && (_jsxs("div", { children: [_jsx("span", { className: "uppercase tracking-wide font-semibold mr-1", children: "Total:" }), toolCall.tokenUsage.totalTokens.toLocaleString()] }))] })), toolCall.startedAt && (_jsxs("div", { className: "flex gap-3 ml-auto", children: [_jsxs("span", { children: ["Started: ", new Date(toolCall.startedAt).toLocaleTimeString()] }), toolCall.completedAt && (_jsxs("span", { children: ["Completed:", " ", new Date(toolCall.completedAt).toLocaleTimeString(), " (", Math.round((toolCall.completedAt - toolCall.startedAt) / 1000), "s)"] }))] }))] }))] }))] }));
|
|
140
|
+
}), toolCall.error && (_jsxs("div", { className: "text-destructive font-mono text-[11px] mt-2", children: ["Error: ", toolCall.error] }))] })] })) : null, toolCall._meta?.truncationWarning && (_jsxs("div", { className: "mx-3 mt-3 mb-0 flex items-center gap-2 rounded-md bg-yellow-50 dark:bg-yellow-950/20 px-3 py-2 text-[11px] text-yellow-800 dark:text-yellow-200 border border-yellow-200 dark:border-yellow-900", children: [_jsx("span", { className: "text-yellow-600 dark:text-yellow-500", children: "\u26A0\uFE0F" }), _jsx("span", { children: toolCall._meta.truncationWarning })] })), (toolCall.tokenUsage || toolCall.startedAt) && (_jsxs("div", { className: "p-2 bg-muted/50 border-t border-border flex flex-wrap gap-4 text-[10px] text-muted-foreground font-sans", children: [toolCall.tokenUsage && (_jsxs("div", { className: "flex gap-3", children: [toolCall.tokenUsage.inputTokens !== undefined && (_jsxs("div", { children: [_jsx("span", { className: "uppercase tracking-wide font-semibold mr-1", children: "Input:" }), toolCall.tokenUsage.inputTokens.toLocaleString()] })), toolCall.tokenUsage.outputTokens !== undefined && (_jsxs("div", { children: [_jsx("span", { className: "uppercase tracking-wide font-semibold mr-1", children: "Output:" }), toolCall.tokenUsage.outputTokens.toLocaleString()] })), toolCall.tokenUsage.totalTokens !== undefined && (_jsxs("div", { children: [_jsx("span", { className: "uppercase tracking-wide font-semibold mr-1", children: "Total:" }), toolCall.tokenUsage.totalTokens.toLocaleString()] }))] })), toolCall.startedAt && (_jsxs("div", { className: "flex gap-3 ml-auto", children: [_jsxs("span", { children: ["Started: ", new Date(toolCall.startedAt).toLocaleTimeString()] }), toolCall.completedAt && (_jsxs("span", { children: ["Completed:", " ", new Date(toolCall.completedAt).toLocaleTimeString(), " (", Math.round((toolCall.completedAt - toolCall.startedAt) / 1000), "s)"] }))] }))] }))] }))] }));
|
|
141
141
|
}
|
|
@@ -76,6 +76,18 @@ export declare class AcpClient {
|
|
|
76
76
|
* Subscribe to errors
|
|
77
77
|
*/
|
|
78
78
|
onError(handler: (error: Error) => void): () => void;
|
|
79
|
+
/**
|
|
80
|
+
* Get agent information
|
|
81
|
+
* - displayName: Human-readable name for UI (preferred)
|
|
82
|
+
* - name: Programmatic name (fallback if displayName not set)
|
|
83
|
+
*/
|
|
84
|
+
getAgentInfo(): {
|
|
85
|
+
name?: string;
|
|
86
|
+
displayName?: string;
|
|
87
|
+
version?: string;
|
|
88
|
+
description?: string;
|
|
89
|
+
suggestedPrompts?: string[];
|
|
90
|
+
};
|
|
79
91
|
/**
|
|
80
92
|
* Create transport based on explicit configuration
|
|
81
93
|
*/
|
|
@@ -57,6 +57,7 @@ export class AcpClient {
|
|
|
57
57
|
if (transportSessionId) {
|
|
58
58
|
// Use the session ID from the transport
|
|
59
59
|
const now = new Date().toISOString();
|
|
60
|
+
const agentName = this.transport.getAgentInfo?.()?.name;
|
|
60
61
|
const session = {
|
|
61
62
|
id: transportSessionId,
|
|
62
63
|
status: "connected",
|
|
@@ -66,6 +67,7 @@ export class AcpClient {
|
|
|
66
67
|
messages: [],
|
|
67
68
|
metadata: {
|
|
68
69
|
startedAt: now,
|
|
70
|
+
agentName,
|
|
69
71
|
},
|
|
70
72
|
};
|
|
71
73
|
this.sessions.set(transportSessionId, session);
|
|
@@ -75,6 +77,7 @@ export class AcpClient {
|
|
|
75
77
|
// Fallback: generate session ID (for transports that don't auto-create sessions)
|
|
76
78
|
const sessionId = this.generateSessionId();
|
|
77
79
|
const now = new Date().toISOString();
|
|
80
|
+
const agentName = this.transport.getAgentInfo?.()?.name;
|
|
78
81
|
const session = {
|
|
79
82
|
id: sessionId,
|
|
80
83
|
status: "connecting",
|
|
@@ -84,6 +87,7 @@ export class AcpClient {
|
|
|
84
87
|
messages: [],
|
|
85
88
|
metadata: {
|
|
86
89
|
startedAt: now,
|
|
90
|
+
agentName,
|
|
87
91
|
},
|
|
88
92
|
};
|
|
89
93
|
this.sessions.set(sessionId, session);
|
|
@@ -116,6 +120,13 @@ export class AcpClient {
|
|
|
116
120
|
this.currentSessionId = sessionId;
|
|
117
121
|
// Load session from transport (will replay messages via session updates)
|
|
118
122
|
await this.transport.loadSession(sessionId);
|
|
123
|
+
// Update metadata with agent name if available
|
|
124
|
+
if (this.transport.getAgentInfo) {
|
|
125
|
+
const agentInfo = this.transport.getAgentInfo();
|
|
126
|
+
if (agentInfo.name && session.metadata) {
|
|
127
|
+
session.metadata.agentName = agentInfo.name;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
119
130
|
// Update session status
|
|
120
131
|
this.updateSessionStatus(sessionId, "connected");
|
|
121
132
|
return sessionId;
|
|
@@ -200,6 +211,14 @@ export class AcpClient {
|
|
|
200
211
|
this.errorHandlers.delete(handler);
|
|
201
212
|
};
|
|
202
213
|
}
|
|
214
|
+
/**
|
|
215
|
+
* Get agent information
|
|
216
|
+
* - displayName: Human-readable name for UI (preferred)
|
|
217
|
+
* - name: Programmatic name (fallback if displayName not set)
|
|
218
|
+
*/
|
|
219
|
+
getAgentInfo() {
|
|
220
|
+
return this.transport.getAgentInfo?.() || {};
|
|
221
|
+
}
|
|
203
222
|
/**
|
|
204
223
|
* Create transport based on explicit configuration
|
|
205
224
|
*/
|
|
@@ -13,9 +13,9 @@ export type MessageRole = z.infer<typeof MessageRole>;
|
|
|
13
13
|
* Content type for messages
|
|
14
14
|
*/
|
|
15
15
|
export declare const ContentType: z.ZodEnum<{
|
|
16
|
-
file: "file";
|
|
17
16
|
text: "text";
|
|
18
17
|
image: "image";
|
|
18
|
+
file: "file";
|
|
19
19
|
tool_call: "tool_call";
|
|
20
20
|
tool_result: "tool_result";
|
|
21
21
|
}>;
|
|
@@ -25,9 +25,9 @@ export type ContentType = z.infer<typeof ContentType>;
|
|
|
25
25
|
*/
|
|
26
26
|
export declare const BaseContent: z.ZodObject<{
|
|
27
27
|
type: z.ZodEnum<{
|
|
28
|
-
file: "file";
|
|
29
28
|
text: "text";
|
|
30
29
|
image: "image";
|
|
30
|
+
file: "file";
|
|
31
31
|
tool_call: "tool_call";
|
|
32
32
|
tool_result: "tool_result";
|
|
33
33
|
}>;
|
|
@@ -197,5 +197,16 @@ export declare const MessageChunk: z.ZodObject<{
|
|
|
197
197
|
totalTokens: z.ZodOptional<z.ZodNumber>;
|
|
198
198
|
}, z.core.$strip>>;
|
|
199
199
|
contextInputTokens: z.ZodOptional<z.ZodNumber>;
|
|
200
|
+
_meta: z.ZodOptional<z.ZodObject<{
|
|
201
|
+
context_size: z.ZodOptional<z.ZodObject<{
|
|
202
|
+
systemPromptTokens: z.ZodNumber;
|
|
203
|
+
userMessagesTokens: z.ZodNumber;
|
|
204
|
+
assistantMessagesTokens: z.ZodNumber;
|
|
205
|
+
toolInputTokens: z.ZodNumber;
|
|
206
|
+
toolResultsTokens: z.ZodNumber;
|
|
207
|
+
totalEstimated: z.ZodNumber;
|
|
208
|
+
llmReportedInputTokens: z.ZodOptional<z.ZodNumber>;
|
|
209
|
+
}, z.core.$strip>>;
|
|
210
|
+
}, z.core.$strip>>;
|
|
200
211
|
}, z.core.$strip>;
|
|
201
212
|
export type MessageChunk = z.infer<typeof MessageChunk>;
|
|
@@ -100,4 +100,19 @@ export const MessageChunk = z.object({
|
|
|
100
100
|
})
|
|
101
101
|
.optional(),
|
|
102
102
|
contextInputTokens: z.number().optional(),
|
|
103
|
+
_meta: z
|
|
104
|
+
.object({
|
|
105
|
+
context_size: z
|
|
106
|
+
.object({
|
|
107
|
+
systemPromptTokens: z.number(),
|
|
108
|
+
userMessagesTokens: z.number(),
|
|
109
|
+
assistantMessagesTokens: z.number(),
|
|
110
|
+
toolInputTokens: z.number(),
|
|
111
|
+
toolResultsTokens: z.number(),
|
|
112
|
+
totalEstimated: z.number(),
|
|
113
|
+
llmReportedInputTokens: z.number().optional(),
|
|
114
|
+
})
|
|
115
|
+
.optional(),
|
|
116
|
+
})
|
|
117
|
+
.optional(),
|
|
103
118
|
});
|
|
@@ -3,12 +3,12 @@ import { z } from "zod";
|
|
|
3
3
|
* Session status
|
|
4
4
|
*/
|
|
5
5
|
export declare const SessionStatus: z.ZodEnum<{
|
|
6
|
-
error: "error";
|
|
7
6
|
idle: "idle";
|
|
8
7
|
connecting: "connecting";
|
|
9
8
|
connected: "connected";
|
|
10
9
|
active: "active";
|
|
11
10
|
streaming: "streaming";
|
|
11
|
+
error: "error";
|
|
12
12
|
disconnected: "disconnected";
|
|
13
13
|
}>;
|
|
14
14
|
export type SessionStatus = z.infer<typeof SessionStatus>;
|
|
@@ -40,12 +40,12 @@ export type SessionMetadata = z.infer<typeof SessionMetadata>;
|
|
|
40
40
|
export declare const Session: z.ZodObject<{
|
|
41
41
|
id: z.ZodString;
|
|
42
42
|
status: z.ZodEnum<{
|
|
43
|
-
error: "error";
|
|
44
43
|
idle: "idle";
|
|
45
44
|
connecting: "connecting";
|
|
46
45
|
connected: "connected";
|
|
47
46
|
active: "active";
|
|
48
47
|
streaming: "streaming";
|
|
48
|
+
error: "error";
|
|
49
49
|
disconnected: "disconnected";
|
|
50
50
|
}>;
|
|
51
51
|
config: z.ZodObject<{
|
|
@@ -108,12 +108,12 @@ export type Session = z.infer<typeof Session>;
|
|
|
108
108
|
export declare const SessionUpdate: z.ZodUnion<readonly [z.ZodObject<{
|
|
109
109
|
sessionId: z.ZodString;
|
|
110
110
|
status: z.ZodOptional<z.ZodEnum<{
|
|
111
|
-
error: "error";
|
|
112
111
|
idle: "idle";
|
|
113
112
|
connecting: "connecting";
|
|
114
113
|
connected: "connected";
|
|
115
114
|
active: "active";
|
|
116
115
|
streaming: "streaming";
|
|
116
|
+
error: "error";
|
|
117
117
|
disconnected: "disconnected";
|
|
118
118
|
}>>;
|
|
119
119
|
message: z.ZodOptional<z.ZodObject<{
|
|
@@ -154,6 +154,7 @@ export declare const SessionUpdate: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
154
154
|
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
155
155
|
}, z.core.$strip>>;
|
|
156
156
|
error: z.ZodOptional<z.ZodString>;
|
|
157
|
+
_meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
157
158
|
type: z.ZodLiteral<"tool_call">;
|
|
158
159
|
toolCall: z.ZodObject<{
|
|
159
160
|
id: z.ZodString;
|
|
@@ -212,17 +213,26 @@ export declare const SessionUpdate: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
212
213
|
outputTokens: z.ZodOptional<z.ZodNumber>;
|
|
213
214
|
totalTokens: z.ZodOptional<z.ZodNumber>;
|
|
214
215
|
}, z.core.$strip>>;
|
|
216
|
+
_meta: z.ZodOptional<z.ZodObject<{
|
|
217
|
+
truncationWarning: z.ZodOptional<z.ZodString>;
|
|
218
|
+
compactionAction: z.ZodOptional<z.ZodEnum<{
|
|
219
|
+
compacted: "compacted";
|
|
220
|
+
truncated: "truncated";
|
|
221
|
+
}>>;
|
|
222
|
+
originalTokens: z.ZodOptional<z.ZodNumber>;
|
|
223
|
+
finalTokens: z.ZodOptional<z.ZodNumber>;
|
|
224
|
+
}, z.core.$strip>>;
|
|
215
225
|
}, z.core.$strip>;
|
|
216
226
|
messageId: z.ZodOptional<z.ZodString>;
|
|
217
227
|
}, z.core.$strip>, z.ZodObject<{
|
|
218
228
|
sessionId: z.ZodString;
|
|
219
229
|
status: z.ZodOptional<z.ZodEnum<{
|
|
220
|
-
error: "error";
|
|
221
230
|
idle: "idle";
|
|
222
231
|
connecting: "connecting";
|
|
223
232
|
connected: "connected";
|
|
224
233
|
active: "active";
|
|
225
234
|
streaming: "streaming";
|
|
235
|
+
error: "error";
|
|
226
236
|
disconnected: "disconnected";
|
|
227
237
|
}>>;
|
|
228
238
|
message: z.ZodOptional<z.ZodObject<{
|
|
@@ -263,6 +273,7 @@ export declare const SessionUpdate: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
263
273
|
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
264
274
|
}, z.core.$strip>>;
|
|
265
275
|
error: z.ZodOptional<z.ZodString>;
|
|
276
|
+
_meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
266
277
|
type: z.ZodLiteral<"tool_call_update">;
|
|
267
278
|
toolCallUpdate: z.ZodObject<{
|
|
268
279
|
id: z.ZodString;
|
|
@@ -308,12 +319,12 @@ export declare const SessionUpdate: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
308
319
|
}, z.core.$strip>, z.ZodObject<{
|
|
309
320
|
sessionId: z.ZodString;
|
|
310
321
|
status: z.ZodOptional<z.ZodEnum<{
|
|
311
|
-
error: "error";
|
|
312
322
|
idle: "idle";
|
|
313
323
|
connecting: "connecting";
|
|
314
324
|
connected: "connected";
|
|
315
325
|
active: "active";
|
|
316
326
|
streaming: "streaming";
|
|
327
|
+
error: "error";
|
|
317
328
|
disconnected: "disconnected";
|
|
318
329
|
}>>;
|
|
319
330
|
message: z.ZodOptional<z.ZodObject<{
|
|
@@ -354,6 +365,7 @@ export declare const SessionUpdate: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
354
365
|
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
355
366
|
}, z.core.$strip>>;
|
|
356
367
|
error: z.ZodOptional<z.ZodString>;
|
|
368
|
+
_meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
357
369
|
type: z.ZodLiteral<"tool_output">;
|
|
358
370
|
toolOutput: z.ZodObject<{
|
|
359
371
|
id: z.ZodString;
|
|
@@ -364,12 +376,12 @@ export declare const SessionUpdate: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
364
376
|
}, z.core.$strip>, z.ZodObject<{
|
|
365
377
|
sessionId: z.ZodString;
|
|
366
378
|
status: z.ZodOptional<z.ZodEnum<{
|
|
367
|
-
error: "error";
|
|
368
379
|
idle: "idle";
|
|
369
380
|
connecting: "connecting";
|
|
370
381
|
connected: "connected";
|
|
371
382
|
active: "active";
|
|
372
383
|
streaming: "streaming";
|
|
384
|
+
error: "error";
|
|
373
385
|
disconnected: "disconnected";
|
|
374
386
|
}>>;
|
|
375
387
|
message: z.ZodOptional<z.ZodObject<{
|
|
@@ -410,6 +422,7 @@ export declare const SessionUpdate: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
410
422
|
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
411
423
|
}, z.core.$strip>>;
|
|
412
424
|
error: z.ZodOptional<z.ZodString>;
|
|
425
|
+
_meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
413
426
|
type: z.ZodOptional<z.ZodLiteral<"generic">>;
|
|
414
427
|
}, z.core.$strip>]>;
|
|
415
428
|
export type SessionUpdate = z.infer<typeof SessionUpdate>;
|
|
@@ -52,6 +52,7 @@ const BaseSessionUpdate = z.object({
|
|
|
52
52
|
status: SessionStatus.optional(),
|
|
53
53
|
message: Message.optional(),
|
|
54
54
|
error: z.string().optional(),
|
|
55
|
+
_meta: z.record(z.string(), z.unknown()).optional(),
|
|
55
56
|
});
|
|
56
57
|
/**
|
|
57
58
|
* Session update with tool call (sessionUpdate: "tool_call")
|
|
@@ -21,6 +21,7 @@ export declare class HttpTransport implements Transport {
|
|
|
21
21
|
private options;
|
|
22
22
|
private isReceivingMessages;
|
|
23
23
|
private isInReplayMode;
|
|
24
|
+
private agentInfo?;
|
|
24
25
|
constructor(options: HttpTransportOptions);
|
|
25
26
|
connect(): Promise<void>;
|
|
26
27
|
/**
|
|
@@ -34,6 +35,13 @@ export declare class HttpTransport implements Transport {
|
|
|
34
35
|
isConnected(): boolean;
|
|
35
36
|
onSessionUpdate(callback: (update: SessionUpdate) => void): () => void;
|
|
36
37
|
onError(callback: (error: Error) => void): () => void;
|
|
38
|
+
getAgentInfo(): {
|
|
39
|
+
name?: string;
|
|
40
|
+
displayName?: string;
|
|
41
|
+
version?: string;
|
|
42
|
+
description?: string;
|
|
43
|
+
suggestedPrompts?: string[];
|
|
44
|
+
};
|
|
37
45
|
/**
|
|
38
46
|
* Send an ACP RPC request to the server
|
|
39
47
|
*/
|
|
@@ -22,6 +22,7 @@ export class HttpTransport {
|
|
|
22
22
|
options;
|
|
23
23
|
isReceivingMessages = false;
|
|
24
24
|
isInReplayMode = false; // True during session replay, ignores non-replay streaming
|
|
25
|
+
agentInfo;
|
|
25
26
|
constructor(options) {
|
|
26
27
|
// Ensure baseUrl doesn't end with a slash
|
|
27
28
|
this.options = { ...options, baseUrl: options.baseUrl.replace(/\/$/, "") };
|
|
@@ -43,6 +44,30 @@ export class HttpTransport {
|
|
|
43
44
|
},
|
|
44
45
|
};
|
|
45
46
|
const initResponse = await this.sendRpcRequest("initialize", initRequest);
|
|
47
|
+
if (initResponse.agentInfo) {
|
|
48
|
+
// Read description and suggestedPrompts from _meta extension point (ACP protocol extension)
|
|
49
|
+
const description = initResponse._meta &&
|
|
50
|
+
typeof initResponse._meta === "object" &&
|
|
51
|
+
"agentDescription" in initResponse._meta
|
|
52
|
+
? String(initResponse._meta.agentDescription)
|
|
53
|
+
: undefined;
|
|
54
|
+
const suggestedPrompts = initResponse._meta &&
|
|
55
|
+
typeof initResponse._meta === "object" &&
|
|
56
|
+
"suggestedPrompts" in initResponse._meta &&
|
|
57
|
+
Array.isArray(initResponse._meta.suggestedPrompts)
|
|
58
|
+
? initResponse._meta.suggestedPrompts
|
|
59
|
+
: undefined;
|
|
60
|
+
this.agentInfo = {
|
|
61
|
+
name: initResponse.agentInfo.name,
|
|
62
|
+
// title is the ACP field for human-readable display name
|
|
63
|
+
...(initResponse.agentInfo.title
|
|
64
|
+
? { displayName: initResponse.agentInfo.title }
|
|
65
|
+
: {}),
|
|
66
|
+
version: initResponse.agentInfo.version,
|
|
67
|
+
...(description ? { description } : {}),
|
|
68
|
+
...(suggestedPrompts ? { suggestedPrompts } : {}),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
46
71
|
logger.info("ACP connection initialized", { initResponse });
|
|
47
72
|
// Step 2: Create a new session
|
|
48
73
|
const sessionRequest = {
|
|
@@ -89,6 +114,30 @@ export class HttpTransport {
|
|
|
89
114
|
};
|
|
90
115
|
logger.info("Loading session - initializing connection", { sessionId });
|
|
91
116
|
const initResponse = await this.sendRpcRequest("initialize", initRequest);
|
|
117
|
+
if (initResponse.agentInfo) {
|
|
118
|
+
// Read description and suggestedPrompts from _meta extension point (ACP protocol extension)
|
|
119
|
+
const description = initResponse._meta &&
|
|
120
|
+
typeof initResponse._meta === "object" &&
|
|
121
|
+
"agentDescription" in initResponse._meta
|
|
122
|
+
? String(initResponse._meta.agentDescription)
|
|
123
|
+
: undefined;
|
|
124
|
+
const suggestedPrompts = initResponse._meta &&
|
|
125
|
+
typeof initResponse._meta === "object" &&
|
|
126
|
+
"suggestedPrompts" in initResponse._meta &&
|
|
127
|
+
Array.isArray(initResponse._meta.suggestedPrompts)
|
|
128
|
+
? initResponse._meta.suggestedPrompts
|
|
129
|
+
: undefined;
|
|
130
|
+
this.agentInfo = {
|
|
131
|
+
name: initResponse.agentInfo.name,
|
|
132
|
+
// title is the ACP field for human-readable display name
|
|
133
|
+
...(initResponse.agentInfo.title
|
|
134
|
+
? { displayName: initResponse.agentInfo.title }
|
|
135
|
+
: {}),
|
|
136
|
+
version: initResponse.agentInfo.version,
|
|
137
|
+
...(description ? { description } : {}),
|
|
138
|
+
...(suggestedPrompts ? { suggestedPrompts } : {}),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
92
141
|
// Check if loadSession is supported
|
|
93
142
|
if (!initResponse.agentCapabilities?.loadSession) {
|
|
94
143
|
logger.error("Agent does not support loading sessions", {
|
|
@@ -287,6 +336,9 @@ export class HttpTransport {
|
|
|
287
336
|
this.errorCallbacks.delete(callback);
|
|
288
337
|
};
|
|
289
338
|
}
|
|
339
|
+
getAgentInfo() {
|
|
340
|
+
return this.agentInfo || {};
|
|
341
|
+
}
|
|
290
342
|
/**
|
|
291
343
|
* Send an ACP RPC request to the server
|
|
292
344
|
*/
|
|
@@ -759,12 +811,6 @@ export class HttpTransport {
|
|
|
759
811
|
if (this.isInReplayMode && !isReplay) {
|
|
760
812
|
return;
|
|
761
813
|
}
|
|
762
|
-
// Handle agent message chunks
|
|
763
|
-
const sessionUpdate = {
|
|
764
|
-
type: "generic",
|
|
765
|
-
sessionId,
|
|
766
|
-
status: "active",
|
|
767
|
-
};
|
|
768
814
|
// Extract token usage from _meta if present
|
|
769
815
|
const tokenUsage = update._meta &&
|
|
770
816
|
typeof update._meta === "object" &&
|
|
@@ -778,6 +824,19 @@ export class HttpTransport {
|
|
|
778
824
|
typeof update._meta.contextInputTokens === "number"
|
|
779
825
|
? update._meta.contextInputTokens
|
|
780
826
|
: undefined;
|
|
827
|
+
// Extract context_size from _meta if present
|
|
828
|
+
const context_size = update._meta &&
|
|
829
|
+
typeof update._meta === "object" &&
|
|
830
|
+
"context_size" in update._meta
|
|
831
|
+
? update._meta.context_size
|
|
832
|
+
: undefined;
|
|
833
|
+
// Handle agent message chunks
|
|
834
|
+
const sessionUpdate = {
|
|
835
|
+
type: "generic",
|
|
836
|
+
sessionId,
|
|
837
|
+
status: "active",
|
|
838
|
+
_meta: update._meta,
|
|
839
|
+
};
|
|
781
840
|
// Queue message chunks if present (but skip during replay)
|
|
782
841
|
// For agent_message_chunk, content is an object, not an array
|
|
783
842
|
const content = update.content;
|
|
@@ -791,6 +850,7 @@ export class HttpTransport {
|
|
|
791
850
|
contentDelta: { type: "text", text: contentObj.text },
|
|
792
851
|
tokenUsage,
|
|
793
852
|
contextInputTokens,
|
|
853
|
+
_meta: context_size ? { context_size } : undefined,
|
|
794
854
|
isComplete: false,
|
|
795
855
|
};
|
|
796
856
|
}
|
|
@@ -832,8 +892,11 @@ export class HttpTransport {
|
|
|
832
892
|
this.notifySessionUpdate(messageSessionUpdate);
|
|
833
893
|
}
|
|
834
894
|
}
|
|
835
|
-
//
|
|
836
|
-
|
|
895
|
+
// Send session update for:
|
|
896
|
+
// 1. Live streaming (not replay)
|
|
897
|
+
// 2. Replay messages with context_size (need to restore context on session load)
|
|
898
|
+
const hasContextSize = sessionUpdate._meta && "context_size" in sessionUpdate._meta;
|
|
899
|
+
if (!isReplay || hasContextSize) {
|
|
837
900
|
this.notifySessionUpdate(sessionUpdate);
|
|
838
901
|
}
|
|
839
902
|
}
|
|
@@ -15,6 +15,7 @@ export declare class StdioTransport implements Transport {
|
|
|
15
15
|
private currentSessionId;
|
|
16
16
|
private chunkResolvers;
|
|
17
17
|
private streamComplete;
|
|
18
|
+
private agentInfo?;
|
|
18
19
|
private originalConsole;
|
|
19
20
|
constructor(options: StdioTransportOptions);
|
|
20
21
|
connect(): Promise<void>;
|
|
@@ -24,6 +25,13 @@ export declare class StdioTransport implements Transport {
|
|
|
24
25
|
isConnected(): boolean;
|
|
25
26
|
onSessionUpdate(callback: (update: SessionUpdate) => void): () => void;
|
|
26
27
|
onError(callback: (error: Error) => void): () => void;
|
|
28
|
+
getAgentInfo(): {
|
|
29
|
+
name?: string;
|
|
30
|
+
displayName?: string;
|
|
31
|
+
version?: string;
|
|
32
|
+
description?: string;
|
|
33
|
+
suggestedPrompts?: string[];
|
|
34
|
+
};
|
|
27
35
|
private notifySessionUpdate;
|
|
28
36
|
private notifyError;
|
|
29
37
|
}
|
|
@@ -18,6 +18,7 @@ export class StdioTransport {
|
|
|
18
18
|
currentSessionId = null;
|
|
19
19
|
chunkResolvers = [];
|
|
20
20
|
streamComplete = false;
|
|
21
|
+
agentInfo;
|
|
21
22
|
originalConsole = null;
|
|
22
23
|
constructor(options) {
|
|
23
24
|
this.options = options;
|
|
@@ -353,6 +354,30 @@ export class StdioTransport {
|
|
|
353
354
|
},
|
|
354
355
|
},
|
|
355
356
|
});
|
|
357
|
+
if (initResponse.agentInfo) {
|
|
358
|
+
// Read description and suggestedPrompts from _meta extension point (ACP protocol extension)
|
|
359
|
+
const description = initResponse._meta &&
|
|
360
|
+
typeof initResponse._meta === "object" &&
|
|
361
|
+
"agentDescription" in initResponse._meta
|
|
362
|
+
? String(initResponse._meta.agentDescription)
|
|
363
|
+
: undefined;
|
|
364
|
+
const suggestedPrompts = initResponse._meta &&
|
|
365
|
+
typeof initResponse._meta === "object" &&
|
|
366
|
+
"suggestedPrompts" in initResponse._meta &&
|
|
367
|
+
Array.isArray(initResponse._meta.suggestedPrompts)
|
|
368
|
+
? initResponse._meta.suggestedPrompts
|
|
369
|
+
: undefined;
|
|
370
|
+
this.agentInfo = {
|
|
371
|
+
name: initResponse.agentInfo.name,
|
|
372
|
+
// title is the ACP field for human-readable display name
|
|
373
|
+
...(initResponse.agentInfo.title
|
|
374
|
+
? { displayName: initResponse.agentInfo.title }
|
|
375
|
+
: {}),
|
|
376
|
+
version: initResponse.agentInfo.version,
|
|
377
|
+
...(description ? { description } : {}),
|
|
378
|
+
...(suggestedPrompts ? { suggestedPrompts } : {}),
|
|
379
|
+
};
|
|
380
|
+
}
|
|
356
381
|
logger.info("ACP connection initialized", { initResponse });
|
|
357
382
|
this.connected = true;
|
|
358
383
|
}
|
|
@@ -511,6 +536,9 @@ export class StdioTransport {
|
|
|
511
536
|
this.errorCallbacks.delete(callback);
|
|
512
537
|
};
|
|
513
538
|
}
|
|
539
|
+
getAgentInfo() {
|
|
540
|
+
return this.agentInfo || {};
|
|
541
|
+
}
|
|
514
542
|
notifySessionUpdate(update) {
|
|
515
543
|
for (const callback of this.sessionUpdateCallbacks) {
|
|
516
544
|
try {
|
|
@@ -36,6 +36,18 @@ export interface Transport {
|
|
|
36
36
|
* Subscribe to errors
|
|
37
37
|
*/
|
|
38
38
|
onError(callback: (error: Error) => void): () => void;
|
|
39
|
+
/**
|
|
40
|
+
* Get agent information
|
|
41
|
+
* - displayName: Human-readable name for UI (from ACP title field)
|
|
42
|
+
* - name: Programmatic name (fallback if displayName not set)
|
|
43
|
+
*/
|
|
44
|
+
getAgentInfo?(): {
|
|
45
|
+
name?: string;
|
|
46
|
+
displayName?: string;
|
|
47
|
+
version?: string;
|
|
48
|
+
description?: string;
|
|
49
|
+
suggestedPrompts?: string[];
|
|
50
|
+
};
|
|
39
51
|
}
|
|
40
52
|
/**
|
|
41
53
|
* Stdio transport options
|
|
@@ -37,5 +37,5 @@ export function ToolCall({ toolCall }) {
|
|
|
37
37
|
? ((toolCall.tokenUsage.inputTokens / 1_000_000) * 3 +
|
|
38
38
|
(toolCall.tokenUsage.outputTokens / 1_000_000) * 15).toFixed(4)
|
|
39
39
|
: null;
|
|
40
|
-
return (_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: statusColors[toolCall.status], children: statusIndicators[toolCall.status] }), _jsx(Text, { color: "cyan", bold: true, children: "[TOOL]" }), _jsx(Text, { children: toolCall.title }), startTime && (_jsxs(Text, { color: "gray", dimColor: true, children: ["| ", startTime] })), duration && (_jsxs(Text, { color: "gray", dimColor: true, children: ["| ", duration, "s"] })), cost && (_jsxs(Text, { color: "gray", dimColor: true, children: ["| $", cost] }))] }));
|
|
40
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: statusColors[toolCall.status], children: statusIndicators[toolCall.status] }), _jsx(Text, { color: "cyan", bold: true, children: "[TOOL]" }), _jsx(Text, { children: toolCall.title }), startTime && (_jsxs(Text, { color: "gray", dimColor: true, children: ["| ", startTime] })), duration && (_jsxs(Text, { color: "gray", dimColor: true, children: ["| ", duration, "s"] })), cost && (_jsxs(Text, { color: "gray", dimColor: true, children: ["| $", cost] }))] }), toolCall._meta?.truncationWarning && (_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { color: "yellow", bold: true, children: ["\u26A0\uFE0F ", toolCall._meta.truncationWarning] }) }))] }));
|
|
41
41
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@townco/ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.46",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@agentclientprotocol/sdk": "^0.5.1",
|
|
43
|
-
"@townco/core": "0.0.
|
|
43
|
+
"@townco/core": "0.0.24",
|
|
44
44
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
45
45
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
46
46
|
"@radix-ui/react-label": "^2.1.8",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@tailwindcss/postcss": "^4.1.17",
|
|
66
|
-
"@townco/tsconfig": "0.1.
|
|
66
|
+
"@townco/tsconfig": "0.1.43",
|
|
67
67
|
"@types/node": "^24.10.0",
|
|
68
68
|
"@types/react": "^19.2.2",
|
|
69
69
|
"ink": "^6.4.0",
|
package/src/styles/global.css
CHANGED
|
@@ -378,3 +378,18 @@
|
|
|
378
378
|
transform: translateX(0);
|
|
379
379
|
}
|
|
380
380
|
}
|
|
381
|
+
|
|
382
|
+
@keyframes pulse-scale {
|
|
383
|
+
0%, 100% {
|
|
384
|
+
transform: scale(1);
|
|
385
|
+
background-color: var(--color-neutral-500);
|
|
386
|
+
}
|
|
387
|
+
50% {
|
|
388
|
+
transform: scale(.9);
|
|
389
|
+
background-color: var(--color-neutral-500);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.animate-pulse-scale {
|
|
394
|
+
animation: pulse-scale 1s ease-in-out infinite;
|
|
395
|
+
}
|