@townco/ui 0.1.110 → 0.1.112

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.
@@ -158,6 +158,12 @@ export declare function useChatMessages(client: AcpClient | null, startSession:
158
158
  };
159
159
  })[] | undefined;
160
160
  isStreaming?: boolean | undefined;
161
+ _meta?: {
162
+ semanticName?: string | undefined;
163
+ agentDefinitionName?: string | undefined;
164
+ currentActivity?: string | undefined;
165
+ statusGenerating?: boolean | undefined;
166
+ } | undefined;
161
167
  }[] | undefined;
162
168
  subagentStreaming?: boolean | undefined;
163
169
  subagentCompleted?: boolean | undefined;
@@ -0,0 +1,28 @@
1
+ import type { SubagentMessage } from "../schemas/tool-call.js";
2
+ export interface UseSubagentStreamOptions {
3
+ /** Sub-agent HTTP port */
4
+ port: number;
5
+ /** Sub-agent session ID */
6
+ sessionId: string;
7
+ /** Base host (defaults to localhost) */
8
+ host?: string;
9
+ }
10
+ export interface UseSubagentStreamReturn {
11
+ /** Accumulated messages from the sub-agent */
12
+ messages: SubagentMessage[];
13
+ /** Whether the stream is currently active */
14
+ isStreaming: boolean;
15
+ /** Error message if connection failed */
16
+ error: string | null;
17
+ }
18
+ /**
19
+ * Hook to connect directly to a sub-agent's SSE endpoint and stream messages.
20
+ *
21
+ * This hook:
22
+ * - Connects to the sub-agent's HTTP server at the given port
23
+ * - Subscribes to the /events SSE endpoint with the session ID
24
+ * - Parses incoming session/update notifications
25
+ * - Extracts text chunks and tool calls
26
+ * - Returns accumulated messages for display
27
+ */
28
+ export declare function useSubagentStream(options: UseSubagentStreamOptions | null): UseSubagentStreamReturn;
@@ -0,0 +1,256 @@
1
+ import { createLogger } from "@townco/core";
2
+ import { useCallback, useEffect, useRef, useState } from "react";
3
+ const logger = createLogger("subagent-stream");
4
+ /**
5
+ * Hook to connect directly to a sub-agent's SSE endpoint and stream messages.
6
+ *
7
+ * This hook:
8
+ * - Connects to the sub-agent's HTTP server at the given port
9
+ * - Subscribes to the /events SSE endpoint with the session ID
10
+ * - Parses incoming session/update notifications
11
+ * - Extracts text chunks and tool calls
12
+ * - Returns accumulated messages for display
13
+ */
14
+ export function useSubagentStream(options) {
15
+ const [messages, setMessages] = useState([]);
16
+ // Start as streaming=true if options provided, since we're about to connect
17
+ const [_isStreaming, setIsStreaming] = useState(!!options);
18
+ const [hasCompleted, setHasCompleted] = useState(false);
19
+ const [error, setError] = useState(null);
20
+ const abortControllerRef = useRef(null);
21
+ const currentMessageRef = useRef(null);
22
+ const updateTimeoutRef = useRef(null);
23
+ // Throttled update to prevent excessive re-renders
24
+ const scheduleUpdate = useCallback(() => {
25
+ // If there's already a pending timeout, let it handle the update
26
+ // (it will read the latest currentMessageRef value)
27
+ if (updateTimeoutRef.current)
28
+ return;
29
+ updateTimeoutRef.current = setTimeout(() => {
30
+ updateTimeoutRef.current = null;
31
+ if (currentMessageRef.current) {
32
+ setMessages([{ ...currentMessageRef.current }]);
33
+ }
34
+ }, 250); // Batch updates every 250ms
35
+ }, []);
36
+ // Process incoming SSE message from sub-agent
37
+ // Defined BEFORE connectToSubagent so it's available in the closure
38
+ const processSSEMessage = useCallback((data) => {
39
+ try {
40
+ const message = JSON.parse(data);
41
+ logger.debug("Processing SSE message", {
42
+ method: message.method,
43
+ hasParams: !!message.params,
44
+ });
45
+ // Check if this is a session/update notification
46
+ if (message.method === "session/update" && message.params?.update) {
47
+ const update = message.params.update;
48
+ logger.debug("Got session update", {
49
+ sessionUpdate: update.sessionUpdate,
50
+ });
51
+ if (update.sessionUpdate === "agent_message_chunk") {
52
+ // Handle text chunk
53
+ const content = update.content;
54
+ if (content?.type === "text" && typeof content.text === "string") {
55
+ if (currentMessageRef.current) {
56
+ currentMessageRef.current.content += content.text;
57
+ // Add to contentBlocks - append to last text block or create new one
58
+ const blocks = currentMessageRef.current.contentBlocks ?? [];
59
+ const lastBlock = blocks[blocks.length - 1];
60
+ if (lastBlock && lastBlock.type === "text") {
61
+ lastBlock.text += content.text;
62
+ }
63
+ else {
64
+ blocks.push({ type: "text", text: content.text });
65
+ }
66
+ currentMessageRef.current.contentBlocks = blocks;
67
+ scheduleUpdate();
68
+ }
69
+ }
70
+ }
71
+ else if (update.sessionUpdate === "tool_call") {
72
+ // Handle new tool call
73
+ const toolCall = {
74
+ id: update.toolCallId ?? `tc-${Date.now()}`,
75
+ title: update.title ?? "Tool call",
76
+ prettyName: update._meta?.prettyName,
77
+ icon: update._meta?.icon,
78
+ status: update.status ?? "pending",
79
+ content: [],
80
+ };
81
+ if (currentMessageRef.current) {
82
+ currentMessageRef.current.toolCalls = [
83
+ ...(currentMessageRef.current.toolCalls ?? []),
84
+ toolCall,
85
+ ];
86
+ // Add to contentBlocks for interleaved display
87
+ const blocks = currentMessageRef.current.contentBlocks ?? [];
88
+ blocks.push({ type: "tool_call", toolCall });
89
+ currentMessageRef.current.contentBlocks = blocks;
90
+ scheduleUpdate();
91
+ }
92
+ }
93
+ else if (update.sessionUpdate === "tool_call_update") {
94
+ // Handle tool call update (status change, completion)
95
+ if (currentMessageRef.current?.toolCalls) {
96
+ const toolCallId = update.toolCallId;
97
+ const updateToolCall = (tc) => tc.id === toolCallId
98
+ ? {
99
+ ...tc,
100
+ status: update.status ?? tc.status,
101
+ content: update.content ?? tc.content,
102
+ }
103
+ : tc;
104
+ currentMessageRef.current.toolCalls =
105
+ currentMessageRef.current.toolCalls.map(updateToolCall);
106
+ // Also update in contentBlocks
107
+ if (currentMessageRef.current.contentBlocks) {
108
+ currentMessageRef.current.contentBlocks =
109
+ currentMessageRef.current.contentBlocks.map((block) => block.type === "tool_call"
110
+ ? { ...block, toolCall: updateToolCall(block.toolCall) }
111
+ : block);
112
+ }
113
+ scheduleUpdate();
114
+ }
115
+ }
116
+ }
117
+ }
118
+ catch (err) {
119
+ logger.error("Failed to parse sub-agent SSE message", {
120
+ error: err instanceof Error ? err.message : String(err),
121
+ });
122
+ }
123
+ }, [scheduleUpdate]);
124
+ const connectToSubagent = useCallback(async (port, sessionId, host, protocol) => {
125
+ const baseUrl = `${protocol}//${host}:${port}`;
126
+ logger.info("Connecting to sub-agent SSE", { baseUrl, sessionId });
127
+ setIsStreaming(true);
128
+ setError(null);
129
+ // Create abort controller for cleanup
130
+ const abortController = new AbortController();
131
+ abortControllerRef.current = abortController;
132
+ try {
133
+ logger.info("Fetching SSE endpoint", {
134
+ url: `${baseUrl}/events`,
135
+ sessionId,
136
+ });
137
+ const response = await fetch(`${baseUrl}/events`, {
138
+ method: "GET",
139
+ headers: {
140
+ "X-Session-ID": sessionId,
141
+ },
142
+ signal: abortController.signal,
143
+ });
144
+ logger.info("SSE response received", {
145
+ status: response.status,
146
+ ok: response.ok,
147
+ });
148
+ if (!response.ok) {
149
+ throw new Error(`SSE connection failed: HTTP ${response.status}`);
150
+ }
151
+ if (!response.body) {
152
+ throw new Error("Response body is null");
153
+ }
154
+ logger.info("Sub-agent SSE connection opened, starting to read stream");
155
+ // Read the SSE stream
156
+ const reader = response.body.getReader();
157
+ const decoder = new TextDecoder();
158
+ let buffer = "";
159
+ // Initialize current message
160
+ currentMessageRef.current = {
161
+ id: `subagent-${Date.now()}`,
162
+ content: "",
163
+ toolCalls: [],
164
+ contentBlocks: [],
165
+ isStreaming: true,
166
+ };
167
+ setMessages([currentMessageRef.current]);
168
+ while (true) {
169
+ const { done, value } = await reader.read();
170
+ if (done) {
171
+ logger.debug("Sub-agent SSE stream closed");
172
+ break;
173
+ }
174
+ // Decode the chunk and add to buffer
175
+ buffer += decoder.decode(value, { stream: true });
176
+ // Process complete SSE messages
177
+ const lines = buffer.split("\n");
178
+ buffer = lines.pop() || ""; // Keep incomplete line in buffer
179
+ let currentEvent = { event: "message", data: "" };
180
+ for (const line of lines) {
181
+ if (line.startsWith("event:")) {
182
+ currentEvent.event = line.substring(6).trim();
183
+ }
184
+ else if (line.startsWith("data:")) {
185
+ currentEvent.data = line.substring(5).trim();
186
+ }
187
+ else if (line === "") {
188
+ // Empty line signals end of event
189
+ if (currentEvent.event === "message" && currentEvent.data) {
190
+ processSSEMessage(currentEvent.data);
191
+ }
192
+ // Reset for next event
193
+ currentEvent = { event: "message", data: "" };
194
+ }
195
+ }
196
+ }
197
+ }
198
+ catch (err) {
199
+ if (err instanceof Error && err.name === "AbortError") {
200
+ logger.debug("Sub-agent SSE stream aborted");
201
+ }
202
+ else {
203
+ const errorMessage = err instanceof Error
204
+ ? err.message
205
+ : "Failed to connect to sub-agent";
206
+ logger.error("Sub-agent SSE error", { error: errorMessage });
207
+ setError(errorMessage);
208
+ }
209
+ }
210
+ finally {
211
+ // Mark streaming as complete
212
+ if (currentMessageRef.current) {
213
+ currentMessageRef.current.isStreaming = false;
214
+ setMessages((prev) => prev.map((m) => m.id === currentMessageRef.current?.id
215
+ ? { ...m, isStreaming: false }
216
+ : m));
217
+ }
218
+ setHasCompleted(true);
219
+ setIsStreaming(false);
220
+ abortControllerRef.current = null;
221
+ logger.debug("Sub-agent stream completed");
222
+ }
223
+ }, [processSSEMessage]);
224
+ // Extract values from options (memoized to avoid dependency issues)
225
+ const port = options?.port;
226
+ const sessionId = options?.sessionId;
227
+ const host = options?.host ??
228
+ (typeof window !== "undefined" ? window.location.hostname : "localhost");
229
+ const protocol = typeof window !== "undefined" ? window.location.protocol : "http:";
230
+ // Connect when options change
231
+ useEffect(() => {
232
+ if (!port || !sessionId) {
233
+ return;
234
+ }
235
+ // Reset state for new connection
236
+ setMessages([]);
237
+ setError(null);
238
+ setHasCompleted(false);
239
+ setIsStreaming(true);
240
+ connectToSubagent(port, sessionId, host, protocol);
241
+ // Cleanup on unmount or options change
242
+ return () => {
243
+ if (abortControllerRef.current) {
244
+ abortControllerRef.current.abort();
245
+ abortControllerRef.current = null;
246
+ }
247
+ if (updateTimeoutRef.current) {
248
+ clearTimeout(updateTimeoutRef.current);
249
+ updateTimeoutRef.current = null;
250
+ }
251
+ };
252
+ }, [port, sessionId, host, protocol, connectToSubagent]);
253
+ // Derive streaming status: streaming if we haven't completed yet
254
+ const effectiveIsStreaming = !hasCompleted;
255
+ return { messages, isStreaming: effectiveIsStreaming, error };
256
+ }
@@ -155,6 +155,12 @@ export declare function useToolCalls(client: AcpClient | null): {
155
155
  };
156
156
  })[] | undefined;
157
157
  isStreaming?: boolean | undefined;
158
+ _meta?: {
159
+ semanticName?: string | undefined;
160
+ agentDefinitionName?: string | undefined;
161
+ currentActivity?: string | undefined;
162
+ statusGenerating?: boolean | undefined;
163
+ } | undefined;
158
164
  }[] | undefined;
159
165
  subagentStreaming?: boolean | undefined;
160
166
  subagentCompleted?: boolean | undefined;
@@ -306,6 +312,12 @@ export declare function useToolCalls(client: AcpClient | null): {
306
312
  };
307
313
  })[] | undefined;
308
314
  isStreaming?: boolean | undefined;
315
+ _meta?: {
316
+ semanticName?: string | undefined;
317
+ agentDefinitionName?: string | undefined;
318
+ currentActivity?: string | undefined;
319
+ statusGenerating?: boolean | undefined;
320
+ } | undefined;
309
321
  }[] | undefined;
310
322
  subagentStreaming?: boolean | undefined;
311
323
  subagentCompleted?: boolean | undefined;
@@ -237,6 +237,12 @@ export declare const DisplayMessage: z.ZodObject<{
237
237
  }, z.core.$strip>;
238
238
  }, z.core.$strip>], "type">>>;
239
239
  isStreaming: z.ZodOptional<z.ZodBoolean>;
240
+ _meta: z.ZodOptional<z.ZodObject<{
241
+ semanticName: z.ZodOptional<z.ZodString>;
242
+ agentDefinitionName: z.ZodOptional<z.ZodString>;
243
+ currentActivity: z.ZodOptional<z.ZodString>;
244
+ statusGenerating: z.ZodOptional<z.ZodBoolean>;
245
+ }, z.core.$strip>>;
240
246
  }, z.core.$strip>>>;
241
247
  subagentStreaming: z.ZodOptional<z.ZodBoolean>;
242
248
  subagentCompleted: z.ZodOptional<z.ZodBoolean>;
@@ -502,6 +508,12 @@ export declare const ChatSessionState: z.ZodObject<{
502
508
  }, z.core.$strip>;
503
509
  }, z.core.$strip>], "type">>>;
504
510
  isStreaming: z.ZodOptional<z.ZodBoolean>;
511
+ _meta: z.ZodOptional<z.ZodObject<{
512
+ semanticName: z.ZodOptional<z.ZodString>;
513
+ agentDefinitionName: z.ZodOptional<z.ZodString>;
514
+ currentActivity: z.ZodOptional<z.ZodString>;
515
+ statusGenerating: z.ZodOptional<z.ZodBoolean>;
516
+ }, z.core.$strip>>;
505
517
  }, z.core.$strip>>>;
506
518
  subagentStreaming: z.ZodOptional<z.ZodBoolean>;
507
519
  subagentCompleted: z.ZodOptional<z.ZodBoolean>;
@@ -262,6 +262,12 @@ export declare const SubagentMessageSchema: z.ZodObject<{
262
262
  }, z.core.$strip>;
263
263
  }, z.core.$strip>], "type">>>;
264
264
  isStreaming: z.ZodOptional<z.ZodBoolean>;
265
+ _meta: z.ZodOptional<z.ZodObject<{
266
+ semanticName: z.ZodOptional<z.ZodString>;
267
+ agentDefinitionName: z.ZodOptional<z.ZodString>;
268
+ currentActivity: z.ZodOptional<z.ZodString>;
269
+ statusGenerating: z.ZodOptional<z.ZodBoolean>;
270
+ }, z.core.$strip>>;
265
271
  }, z.core.$strip>;
266
272
  export type SubagentMessage = z.infer<typeof SubagentMessageSchema>;
267
273
  /**
@@ -443,6 +449,12 @@ export declare const ToolCallSchema: z.ZodObject<{
443
449
  }, z.core.$strip>;
444
450
  }, z.core.$strip>], "type">>>;
445
451
  isStreaming: z.ZodOptional<z.ZodBoolean>;
452
+ _meta: z.ZodOptional<z.ZodObject<{
453
+ semanticName: z.ZodOptional<z.ZodString>;
454
+ agentDefinitionName: z.ZodOptional<z.ZodString>;
455
+ currentActivity: z.ZodOptional<z.ZodString>;
456
+ statusGenerating: z.ZodOptional<z.ZodBoolean>;
457
+ }, z.core.$strip>>;
446
458
  }, z.core.$strip>>>;
447
459
  subagentStreaming: z.ZodOptional<z.ZodBoolean>;
448
460
  subagentCompleted: z.ZodOptional<z.ZodBoolean>;
@@ -596,6 +608,12 @@ export declare const ToolCallUpdateSchema: z.ZodObject<{
596
608
  }, z.core.$strip>;
597
609
  }, z.core.$strip>], "type">>>;
598
610
  isStreaming: z.ZodOptional<z.ZodBoolean>;
611
+ _meta: z.ZodOptional<z.ZodObject<{
612
+ semanticName: z.ZodOptional<z.ZodString>;
613
+ agentDefinitionName: z.ZodOptional<z.ZodString>;
614
+ currentActivity: z.ZodOptional<z.ZodString>;
615
+ statusGenerating: z.ZodOptional<z.ZodBoolean>;
616
+ }, z.core.$strip>>;
599
617
  }, z.core.$strip>>>;
600
618
  subagentCompleted: z.ZodOptional<z.ZodBoolean>;
601
619
  _meta: z.ZodOptional<z.ZodObject<{
@@ -117,6 +117,14 @@ export const SubagentMessageSchema = z.object({
117
117
  /** Interleaved content blocks in arrival order */
118
118
  contentBlocks: z.array(SubagentContentBlockSchema).optional(),
119
119
  isStreaming: z.boolean().optional(),
120
+ _meta: z
121
+ .object({
122
+ semanticName: z.string().optional(),
123
+ agentDefinitionName: z.string().optional(),
124
+ currentActivity: z.string().optional(),
125
+ statusGenerating: z.boolean().optional(),
126
+ })
127
+ .optional(),
120
128
  });
121
129
  /**
122
130
  * Complete tool call state as displayed in the UI
@@ -0,0 +1,9 @@
1
+ import type { ToolCall as ToolCallType } from "../../core/schemas/tool-call.js";
2
+ export interface InvokingGroupProps {
3
+ toolCalls: ToolCallType[];
4
+ }
5
+ /**
6
+ * InvokingGroup component - displays a group of preliminary (invoking) tool calls
7
+ * Shows as "Invoking parallel operation (N)" with a summary of unique tool names
8
+ */
9
+ export declare function InvokingGroup({ toolCalls }: InvokingGroupProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ListVideo } from "lucide-react";
3
+ import React from "react";
4
+ /**
5
+ * InvokingGroup component - displays a group of preliminary (invoking) tool calls
6
+ * Shows as "Invoking parallel operation (N)" with a summary of unique tool names
7
+ */
8
+ export function InvokingGroup({ toolCalls }) {
9
+ // Get unique display names for the summary
10
+ const displayNames = toolCalls.map((tc) => tc.prettyName || tc.title);
11
+ const uniqueNames = [...new Set(displayNames)];
12
+ const summary = uniqueNames.length <= 2
13
+ ? uniqueNames.join(", ")
14
+ : `${uniqueNames.slice(0, 2).join(", ")} +${uniqueNames.length - 2} more`;
15
+ return (_jsxs("div", { className: "flex flex-col my-4", children: [_jsxs("div", { className: "flex items-center gap-1.5 text-paragraph-sm text-muted-foreground/50", children: [_jsx(ListVideo, { className: "h-3 w-3" }), _jsx("span", { children: "Invoking parallel operation" }), _jsx("span", { className: "text-[10px] bg-muted px-1.5 py-0.5 rounded text-muted-foreground/50", children: toolCalls.length })] }), _jsx("span", { className: "text-paragraph-sm text-muted-foreground/50 pl-4.5", children: summary })] }));
16
+ }
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ChevronDown, Loader2 } from "lucide-react";
3
3
  import { useCallback, useEffect, useRef, useState } from "react";
4
4
  import { MarkdownRenderer } from "./MarkdownRenderer.js";
@@ -93,10 +93,13 @@ export function SubAgentDetails({ parentStatus, agentName, query, isExpanded: co
93
93
  ? (query.split("\n")[0] ?? "").slice(0, 100) +
94
94
  (query.length > 100 ? "..." : "")
95
95
  : "";
96
- return (_jsxs("div", { children: [!isExpanded && (_jsx("div", { className: "w-full max-w-md", children: previewText ? (_jsx("p", { className: "text-paragraph-sm text-muted-foreground/70 truncate", children: previewText })) : queryFirstLine ? (_jsx("p", { className: "text-paragraph-sm text-muted-foreground/50 truncate", children: queryFirstLine })) : null })), isExpanded && (_jsxs("div", { className: "space-y-3", children: [(agentName || query) && (_jsxs("div", { children: [
96
+ return (_jsxs("div", { children: [!isExpanded && (_jsx("div", { className: "w-full max-w-md", children: currentMessage?._meta?.currentActivity ? (_jsx("p", { className: "text-paragraph-sm text-muted-foreground/70 truncate", children: currentMessage._meta.currentActivity })) : currentMessage?._meta?.statusGenerating ? (_jsx("p", { className: "text-paragraph-sm text-muted-foreground/50 truncate animate-pulse", children: "Determining status..." })) : previewText ? (_jsx("p", { className: "text-paragraph-sm text-muted-foreground/70 truncate", children: previewText })) : queryFirstLine ? (_jsx("p", { className: "text-paragraph-sm text-muted-foreground/50 truncate", children: queryFirstLine })) : null })), isExpanded && (_jsxs("div", { className: "space-y-3", children: [(agentName || query) && (_jsxs("div", { children: [
97
97
  _jsx("div", { className: "text-[10px] font-bold text-muted-foreground uppercase tracking-wider mb-1.5 font-sans", children: "Input" }), _jsxs("div", { className: "text-[11px] font-mono space-y-1", children: [agentName && (_jsxs("div", { children: [
98
- _jsx("span", { className: "text-muted-foreground", children: "agentName: " }), _jsx("span", { className: "text-foreground", children: agentName })
99
- ] })), query && (_jsxs("div", { children: [
98
+ _jsx("span", { className: "text-muted-foreground", children: storedMessages?.[0]?._meta?.semanticName
99
+ ? "Task: "
100
+ : "agentName: " }), storedMessages?.[0]?._meta?.semanticName ? (_jsxs(_Fragment, { children: [
101
+ _jsx("span", { className: "text-foreground font-medium", children: storedMessages[0]._meta.semanticName }), _jsxs("span", { className: "text-muted-foreground text-[10px] ml-1", children: ["(", agentName, ")"] })
102
+ ] })) : (_jsx("span", { className: "text-foreground", children: agentName }))] })), query && (_jsxs("div", { children: [
100
103
  _jsx("span", { className: "text-muted-foreground", children: "query: " }), _jsx("span", { className: "text-foreground", children: query })
101
104
  ] }))] })
102
105
  ] })), _jsxs("div", { children: [
@@ -0,0 +1,8 @@
1
+ import type { ToolCall as ToolCallType } from "../../core/schemas/tool-call.js";
2
+ export interface ToolCallProps {
3
+ toolCall: ToolCallType;
4
+ }
5
+ /**
6
+ * ToolCall component - displays a single tool call with collapsible details
7
+ */
8
+ export declare function ToolCall({ toolCall }: ToolCallProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,226 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import JsonView from "@uiw/react-json-view";
3
+ import { AlertCircle, CheckSquare, ChevronDown, ChevronRight, CircleDot, Cloud, Edit, FileText, Globe, Image, Link, Search, Wrench, } from "lucide-react";
4
+ import React, { useState } from "react";
5
+ import { ChatLayout } from "./index.js";
6
+ import { SubAgentDetails } from "./SubAgentDetails.js";
7
+ import { useTheme } from "./ThemeProvider.js";
8
+ /**
9
+ * Map of icon names to Lucide components
10
+ */
11
+ const ICON_MAP = {
12
+ Globe: Globe,
13
+ Image: Image,
14
+ Link: Link,
15
+ Cloud: Cloud,
16
+ CheckSquare: CheckSquare,
17
+ Search: Search,
18
+ FileText: FileText,
19
+ Edit: Edit,
20
+ Wrench: Wrench,
21
+ CircleDot: CircleDot,
22
+ };
23
+ /**
24
+ * Tool call kind icons (using emoji for simplicity)
25
+ */
26
+ const _kindIcons = {
27
+ read: "\u{1F4C4}",
28
+ edit: "\u{270F}\u{FE0F}",
29
+ delete: "\u{1F5D1}\u{FE0F}",
30
+ move: "\u{1F4E6}",
31
+ search: "\u{1F50D}",
32
+ execute: "\u{2699}\u{FE0F}",
33
+ think: "\u{1F4AD}",
34
+ fetch: "\u{1F310}",
35
+ switch_mode: "\u{1F501}",
36
+ other: "\u{1F527}",
37
+ };
38
+ /**
39
+ * ToolCall component - displays a single tool call with collapsible details
40
+ */
41
+ export function ToolCall({ toolCall }) {
42
+ const [isExpanded, setIsExpanded] = useState(false);
43
+ const [isSubagentExpanded, setIsSubagentExpanded] = useState(false);
44
+ const { resolvedTheme } = useTheme();
45
+ // Detect TodoWrite tool and subagent
46
+ const isTodoWrite = toolCall.title === "todo_write";
47
+ const isSubagentCall = !!(toolCall.subagentPort && toolCall.subagentSessionId);
48
+ // Safely access ChatLayout context - will be undefined if not within ChatLayout
49
+ const layoutContext = React.useContext(ChatLayout.Context);
50
+ // Click handler: toggle sidepanel for TodoWrite, subagent details for subagents, expand for others
51
+ const handleHeaderClick = React.useCallback(() => {
52
+ if (isTodoWrite && layoutContext) {
53
+ // Toggle sidepanel - close if already open on todo tab, otherwise open
54
+ if (layoutContext.panelSize !== "hidden" &&
55
+ layoutContext.activeTab === "todo") {
56
+ layoutContext.setPanelSize("hidden");
57
+ }
58
+ else {
59
+ layoutContext.setPanelSize("small");
60
+ layoutContext.setActiveTab("todo");
61
+ }
62
+ }
63
+ else if (isSubagentCall) {
64
+ // Toggle subagent details
65
+ setIsSubagentExpanded(!isSubagentExpanded);
66
+ }
67
+ else {
68
+ // Normal expand/collapse
69
+ setIsExpanded(!isExpanded);
70
+ }
71
+ }, [
72
+ isTodoWrite,
73
+ layoutContext,
74
+ isExpanded,
75
+ isSubagentCall,
76
+ isSubagentExpanded,
77
+ ]);
78
+ // Determine which icon to show
79
+ const IconComponent = toolCall.icon && ICON_MAP[toolCall.icon]
80
+ ? ICON_MAP[toolCall.icon]
81
+ : CircleDot;
82
+ // Determine display name
83
+ const displayName = toolCall.prettyName || toolCall.title;
84
+ // Determine icon color based on status (especially for subagents)
85
+ const isSubagentRunning = isSubagentCall &&
86
+ (toolCall.status === "in_progress" || toolCall.status === "pending");
87
+ const isSubagentFailed = isSubagentCall && toolCall.status === "failed";
88
+ const iconColorClass = isSubagentCall
89
+ ? isSubagentFailed
90
+ ? "text-destructive"
91
+ : isSubagentRunning
92
+ ? "text-foreground animate-pulse"
93
+ : "text-green-500"
94
+ : "text-muted-foreground";
95
+ const statusTooltip = isSubagentCall
96
+ ? isSubagentFailed
97
+ ? "Sub-agent failed"
98
+ : isSubagentRunning
99
+ ? "Sub-agent running"
100
+ : "Sub-agent completed"
101
+ : undefined;
102
+ // Check if there's an error
103
+ const hasError = toolCall.status === "failed" || !!toolCall.error;
104
+ // Check if this is a preliminary (pending) tool call without full details yet
105
+ const isPreliminary = toolCall.status === "pending" &&
106
+ (!toolCall.rawInput || Object.keys(toolCall.rawInput).length === 0);
107
+ // JSON View style based on theme
108
+ const jsonStyle = {
109
+ fontSize: "11px",
110
+ backgroundColor: "transparent",
111
+ fontFamily: "inherit",
112
+ "--w-rjv-color": resolvedTheme === "dark" ? "#fafafa" : "#09090b",
113
+ "--w-rjv-key-string": resolvedTheme === "dark" ? "#fafafa" : "#09090b",
114
+ "--w-rjv-background-color": "transparent",
115
+ "--w-rjv-line-color": resolvedTheme === "dark" ? "#27272a" : "#e4e4e7",
116
+ "--w-rjv-arrow-color": resolvedTheme === "dark" ? "#a1a1aa" : "#71717a",
117
+ "--w-rjv-edit-color": resolvedTheme === "dark" ? "#fafafa" : "#09090b",
118
+ "--w-rjv-info-color": resolvedTheme === "dark" ? "#a1a1aa" : "#71717a",
119
+ "--w-rjv-update-color": resolvedTheme === "dark" ? "#fafafa" : "#09090b",
120
+ "--w-rjv-copied-color": resolvedTheme === "dark" ? "#fafafa" : "#09090b",
121
+ "--w-rjv-copied-success-color": resolvedTheme === "dark" ? "#22c55e" : "#16a34a",
122
+ "--w-rjv-curlybraces-color": resolvedTheme === "dark" ? "#a1a1aa" : "#71717a",
123
+ "--w-rjv-colon-color": resolvedTheme === "dark" ? "#a1a1aa" : "#71717a",
124
+ "--w-rjv-brackets-color": resolvedTheme === "dark" ? "#a1a1aa" : "#71717a",
125
+ "--w-rjv-quotes-color": resolvedTheme === "dark" ? "#a1a1aa" : "#71717a",
126
+ "--w-rjv-quotes-string-color": resolvedTheme === "dark" ? "#a1a1aa" : "#71717a",
127
+ "--w-rjv-type-string-color": resolvedTheme === "dark" ? "#22c55e" : "#16a34a",
128
+ "--w-rjv-type-int-color": resolvedTheme === "dark" ? "#f59e0b" : "#d97706",
129
+ "--w-rjv-type-float-color": resolvedTheme === "dark" ? "#f59e0b" : "#d97706",
130
+ "--w-rjv-type-bigint-color": resolvedTheme === "dark" ? "#f59e0b" : "#d97706",
131
+ "--w-rjv-type-boolean-color": resolvedTheme === "dark" ? "#3b82f6" : "#2563eb",
132
+ "--w-rjv-type-date-color": resolvedTheme === "dark" ? "#ec4899" : "#db2777",
133
+ "--w-rjv-type-url-color": resolvedTheme === "dark" ? "#3b82f6" : "#2563eb",
134
+ "--w-rjv-type-null-color": resolvedTheme === "dark" ? "#ef4444" : "#dc2626",
135
+ "--w-rjv-type-nan-color": resolvedTheme === "dark" ? "#ef4444" : "#dc2626",
136
+ "--w-rjv-type-undefined-color": resolvedTheme === "dark" ? "#ef4444" : "#dc2626",
137
+ };
138
+ // Preliminary tool calls show as simple light gray text without expansion
139
+ if (isPreliminary) {
140
+ return (_jsx("div", { className: "flex flex-col my-4", children: _jsxs("span", { className: "text-paragraph-sm text-muted-foreground/50", children: ["Invoking ", displayName] }) }));
141
+ }
142
+ return (_jsxs("div", { className: "flex flex-col my-4", children: [_jsxs("button", { type: "button", className: "flex flex-col items-start gap-0.5 cursor-pointer bg-transparent border-none p-0 text-left group w-fit", onClick: handleHeaderClick, "aria-expanded": isTodoWrite ? undefined : isExpanded, children: [_jsxs("div", { className: "flex items-center gap-1.5 text-[11px] font-medium text-muted-foreground", children: [_jsx("div", { className: iconColorClass, title: statusTooltip, children: _jsx(IconComponent, { className: "h-3 w-3" }) }), _jsx("span", { className: "text-paragraph-sm text-muted-foreground", children: displayName }), hasError && _jsx(AlertCircle, { className: "h-3 w-3 text-destructive" }), isTodoWrite ? (_jsx(ChevronRight, { className: "h-3 w-3 text-muted-foreground/70" })) : (_jsx(ChevronDown, { className: `h-3 w-3 text-muted-foreground/70 transition-transform duration-200 ${isExpanded ? "rotate-180" : ""}` }))] }), toolCall.subline && (_jsx("span", { className: "text-paragraph-sm text-muted-foreground/70 pl-4.5", children: toolCall.subline }))] }), !isTodoWrite && isSubagentCall && (_jsx("div", { className: "pl-4.5", children: _jsx(SubAgentDetails, { port: toolCall.subagentPort, sessionId: toolCall.subagentSessionId, parentStatus: toolCall.status, agentName: toolCall.rawInput?.agentName, query: toolCall.rawInput?.query, isExpanded: isSubagentExpanded, onExpandChange: setIsSubagentExpanded }) })), !isTodoWrite && !isSubagentCall && isExpanded && (_jsxs("div", { className: "mt-2 text-sm border border-border rounded-lg bg-card overflow-hidden w-full", children: [toolCall.rawInput &&
143
+ Object.keys(toolCall.rawInput).length > 0 &&
144
+ !toolCall.subagentPort && (_jsxs("div", { className: "p-3 border-b border-border", children: [_jsx("div", { className: "text-[10px] font-bold text-muted-foreground uppercase tracking-wider mb-1.5 font-sans", children: "Input" }), _jsx("div", { className: "text-[11px] font-mono text-foreground", children: _jsx(JsonView, { value: toolCall.rawInput, collapsed: false, displayDataTypes: false, displayObjectSize: false, enableClipboard: true, style: jsonStyle }) })] })), toolCall.locations && toolCall.locations.length > 0 && (_jsxs("div", { className: "p-3 border-b border-border", children: [_jsx("div", { className: "text-[10px] font-bold text-muted-foreground uppercase tracking-wider mb-1.5 font-sans", children: "Files" }), _jsx("ul", { className: "space-y-1", children: toolCall.locations.map((loc) => (_jsxs("li", { className: "font-mono text-[11px] text-foreground bg-muted px-1.5 py-0.5 rounded w-fit", children: [loc.path, loc.line !== null &&
145
+ loc.line !== undefined &&
146
+ `:${loc.line}`] }, `${loc.path}:${loc.line ?? ""}`))) })] })), (toolCall.content && toolCall.content.length > 0) ||
147
+ toolCall.error ? (_jsxs("div", { className: "p-3 border-b border-border last:border-0", children: [_jsx("div", { className: "text-[10px] font-bold text-muted-foreground uppercase tracking-wider mb-1.5 font-sans", children: "Output" }), _jsxs("div", { className: "space-y-2 text-[11px] text-foreground", children: [toolCall.content?.map((block, idx) => {
148
+ // Generate a stable key based on content
149
+ const getBlockKey = () => {
150
+ if (block.type === "diff" && "path" in block) {
151
+ return `diff-${block.path}-${idx}`;
152
+ }
153
+ if (block.type === "terminal" && "terminalId" in block) {
154
+ return `terminal-${block.terminalId}`;
155
+ }
156
+ if (block.type === "text" && "text" in block) {
157
+ return `text-${block.text.substring(0, 20)}-${idx}`;
158
+ }
159
+ if (block.type === "content" && "content" in block) {
160
+ const innerContent = block.content;
161
+ return `content-${innerContent.text?.substring(0, 20)}-${idx}`;
162
+ }
163
+ return `block-${idx}`;
164
+ };
165
+ // Helper to render text content (with JSON parsing if applicable)
166
+ const renderTextContent = (text, key) => {
167
+ // Try to parse as JSON
168
+ try {
169
+ const parsed = JSON.parse(text);
170
+ // If it's an object or array, render with JsonView
171
+ if (typeof parsed === "object" && parsed !== null) {
172
+ return (_jsx("div", { className: "text-[11px]", children: _jsx(JsonView, { value: parsed, collapsed: false, displayDataTypes: false, displayObjectSize: false, enableClipboard: true, style: jsonStyle }) }, key));
173
+ }
174
+ }
175
+ catch {
176
+ // Not valid JSON, render as plain text
177
+ }
178
+ // Render as plain text
179
+ return (_jsx("pre", { className: "whitespace-pre-wrap font-mono text-[11px] text-foreground overflow-x-auto", children: text }, key));
180
+ };
181
+ // Handle nested content blocks (ACP format)
182
+ if (block.type === "content" && "content" in block) {
183
+ const innerContent = block.content;
184
+ if (innerContent.type === "text" && innerContent.text) {
185
+ return renderTextContent(innerContent.text, getBlockKey());
186
+ }
187
+ }
188
+ // Handle direct text blocks
189
+ if (block.type === "text" && "text" in block) {
190
+ return renderTextContent(block.text, getBlockKey());
191
+ }
192
+ // Handle image blocks
193
+ if (block.type === "image") {
194
+ const alt = block.alt || "Generated image";
195
+ let imageSrc;
196
+ if ("data" in block) {
197
+ // Base64 encoded image
198
+ const mimeType = block.mimeType || "image/png";
199
+ imageSrc = `data:${mimeType};base64,${block.data}`;
200
+ }
201
+ else if ("url" in block) {
202
+ // URL or file path
203
+ imageSrc = block.url;
204
+ }
205
+ else {
206
+ return null;
207
+ }
208
+ return (_jsx("div", { className: "my-2", children: _jsx("img", { src: imageSrc, alt: alt, className: "max-w-full h-auto rounded-md border border-border" }) }, getBlockKey()));
209
+ }
210
+ // Handle diff blocks
211
+ if (block.type === "diff" &&
212
+ "path" in block &&
213
+ "oldText" in block &&
214
+ "newText" in block) {
215
+ return (_jsxs("div", { className: "border border-border rounded bg-card", children: [_jsxs("div", { className: "bg-muted px-2 py-1 text-[10px] font-mono text-muted-foreground border-b border-border", children: [block.path, "line" in block &&
216
+ block.line !== null &&
217
+ block.line !== undefined &&
218
+ `:${block.line}`] }), _jsxs("div", { className: "p-2 font-mono text-[11px]", children: [_jsxs("div", { className: "text-red-500 dark:text-red-400", children: ["- ", block.oldText] }), _jsxs("div", { className: "text-green-500 dark:text-green-400", children: ["+ ", block.newText] })] })] }, getBlockKey()));
219
+ }
220
+ // Handle terminal blocks
221
+ if (block.type === "terminal" && "terminalId" in block) {
222
+ return (_jsxs("div", { className: "bg-neutral-900 text-neutral-100 p-2 rounded text-[11px] font-mono", children: ["Terminal: ", block.terminalId] }, getBlockKey()));
223
+ }
224
+ return null;
225
+ }), 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)"] }))] }))] }))] }))] }));
226
+ }
@@ -0,0 +1,8 @@
1
+ import type { ToolCall as ToolCallType } from "../../core/schemas/tool-call.js";
2
+ export interface ToolCallGroupProps {
3
+ toolCalls: ToolCallType[];
4
+ }
5
+ /**
6
+ * ToolCallGroup component - displays a group of parallel tool calls with collapsible details
7
+ */
8
+ export declare function ToolCallGroup({ toolCalls }: ToolCallGroupProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ChevronDown, ListVideo } from "lucide-react";
3
+ import React, { useState } from "react";
4
+ import { ToolCall } from "./ToolCall.js";
5
+ /**
6
+ * ToolCallGroup component - displays a group of parallel tool calls with collapsible details
7
+ */
8
+ export function ToolCallGroup({ toolCalls }) {
9
+ const [isExpanded, setIsExpanded] = useState(false);
10
+ // Calculate group status based on individual tool call statuses
11
+ const getGroupStatus = () => {
12
+ const statuses = toolCalls.map((tc) => tc.status);
13
+ if (statuses.some((s) => s === "failed"))
14
+ return "failed";
15
+ if (statuses.some((s) => s === "in_progress"))
16
+ return "in_progress";
17
+ if (statuses.every((s) => s === "completed"))
18
+ return "completed";
19
+ return "pending";
20
+ };
21
+ const groupStatus = getGroupStatus();
22
+ // Generate summary of tool names
23
+ const toolNames = toolCalls.map((tc) => tc.prettyName || tc.title);
24
+ const uniqueNames = [...new Set(toolNames)];
25
+ const summary = uniqueNames.length <= 2
26
+ ? uniqueNames.join(", ")
27
+ : `${uniqueNames.slice(0, 2).join(", ")} +${uniqueNames.length - 2} more`;
28
+ return (_jsxs("div", { className: "flex flex-col my-4", children: [_jsxs("button", { type: "button", className: "flex flex-col items-start gap-0.5 cursor-pointer bg-transparent border-none p-0 text-left group w-fit", onClick: () => setIsExpanded(!isExpanded), "aria-expanded": isExpanded, children: [_jsxs("div", { className: "flex items-center gap-1.5 text-[11px] font-medium text-muted-foreground", children: [_jsx("div", { className: "text-muted-foreground", children: _jsx(ListVideo, { className: "h-3 w-3" }) }), _jsx("span", { className: "text-paragraph-sm text-muted-foreground", children: "Parallel operation" }), _jsx("span", { className: "text-[10px] bg-muted px-1.5 py-0.5 rounded text-muted-foreground/70", children: toolCalls.length }), _jsx(ChevronDown, { className: `h-3 w-3 text-muted-foreground/70 transition-transform duration-200 ${isExpanded ? "rotate-180" : ""}` })] }), !isExpanded && (_jsx("span", { className: "text-paragraph-sm text-muted-foreground/70 pl-4.5", children: summary }))] }), isExpanded && (_jsx("div", { className: "mt-1", children: toolCalls.map((toolCall) => (_jsxs("div", { className: "flex items-start", children: [_jsx("div", { className: "w-2.5 h-4 border-l-2 border-b-2 border-border rounded-bl-[6px] mt-1 mr-0.5 shrink-0" }), _jsx("div", { className: "flex-1 -mt-2", children: _jsx(ToolCall, { toolCall: toolCall }) })] }, toolCall.id))) }))] }));
29
+ }
@@ -349,7 +349,9 @@ function GroupedToolCallItem({ toolCall, hookNotification, }) {
349
349
  // Render subagent with clickable header and SubAgentDetails component
350
350
  return (_jsxs("div", { className: "flex flex-col ml-5", children: [
351
351
  _jsxs("button", { type: "button", className: "flex items-center gap-1.5 cursor-pointer bg-transparent border-none p-0 text-left group w-fit", onClick: () => setIsExpanded(!isExpanded), "aria-expanded": isExpanded, children: [
352
- _jsx("div", { className: "text-text-secondary/70 group-hover:text-text-secondary transition-colors", children: _jsx(CircleDot, { className: "h-3 w-3" }) }), _jsx("span", { className: "text-paragraph-sm text-text-secondary/70 group-hover:text-text-secondary transition-colors", children: toolCall.rawInput?.agentName || "Subagent" }), isRunning && toolCall.startedAt && (_jsx(RunningDuration, { startTime: toolCall.startedAt })), isFailed && (_jsx("span", { title: toolCall.error || "Operation failed", children: _jsx(AlertCircle, { className: "h-3 w-3 text-destructive" }) })), hasCompaction && (_jsx(TooltipProvider, { delayDuration: 0, children: _jsxs(Tooltip, { children: [
352
+ _jsx("div", { className: "text-text-secondary/70 group-hover:text-text-secondary transition-colors", children: _jsx(CircleDot, { className: "h-3 w-3" }) }), _jsx("span", { className: "text-paragraph-sm text-text-secondary/70 group-hover:text-text-secondary transition-colors", children: toolCall.subagentMessages?.[0]?._meta?.semanticName ||
353
+ toolCall.rawInput?.agentName ||
354
+ "Subagent" }), toolCall.subagentMessages?.[0]?._meta?.semanticName && (_jsxs("span", { className: "text-muted-foreground text-[10px] ml-1", children: ["(", toolCall.rawInput?.agentName || "subagent", ")"] })), isRunning && toolCall.startedAt && (_jsx(RunningDuration, { startTime: toolCall.startedAt })), isFailed && (_jsx("span", { title: toolCall.error || "Operation failed", children: _jsx(AlertCircle, { className: "h-3 w-3 text-destructive" }) })), hasCompaction && (_jsx(TooltipProvider, { delayDuration: 0, children: _jsxs(Tooltip, { children: [
353
355
  _jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { children: isTruncation ? (_jsx(ScissorsLineDashed, { className: "h-3 w-3 text-destructive" })) : (_jsx(FoldVertical, { className: "h-3 w-3 text-text-secondary/70" })) }) }), _jsx(TooltipContent, { children: (() => {
354
356
  const meta = toolCall._meta;
355
357
  const percentage = meta?.originalTokens && meta?.finalTokens
@@ -348,6 +348,12 @@ export declare const SessionUpdate: z.ZodUnion<readonly [z.ZodObject<{
348
348
  }, z.core.$strip>;
349
349
  }, z.core.$strip>], "type">>>;
350
350
  isStreaming: z.ZodOptional<z.ZodBoolean>;
351
+ _meta: z.ZodOptional<z.ZodObject<{
352
+ semanticName: z.ZodOptional<z.ZodString>;
353
+ agentDefinitionName: z.ZodOptional<z.ZodString>;
354
+ currentActivity: z.ZodOptional<z.ZodString>;
355
+ statusGenerating: z.ZodOptional<z.ZodBoolean>;
356
+ }, z.core.$strip>>;
351
357
  }, z.core.$strip>>>;
352
358
  subagentStreaming: z.ZodOptional<z.ZodBoolean>;
353
359
  subagentCompleted: z.ZodOptional<z.ZodBoolean>;
@@ -557,6 +563,12 @@ export declare const SessionUpdate: z.ZodUnion<readonly [z.ZodObject<{
557
563
  }, z.core.$strip>;
558
564
  }, z.core.$strip>], "type">>>;
559
565
  isStreaming: z.ZodOptional<z.ZodBoolean>;
566
+ _meta: z.ZodOptional<z.ZodObject<{
567
+ semanticName: z.ZodOptional<z.ZodString>;
568
+ agentDefinitionName: z.ZodOptional<z.ZodString>;
569
+ currentActivity: z.ZodOptional<z.ZodString>;
570
+ statusGenerating: z.ZodOptional<z.ZodBoolean>;
571
+ }, z.core.$strip>>;
560
572
  }, z.core.$strip>>>;
561
573
  subagentCompleted: z.ZodOptional<z.ZodBoolean>;
562
574
  _meta: z.ZodOptional<z.ZodObject<{
@@ -872,19 +872,14 @@ export class HttpTransport {
872
872
  message.method === "session/update" &&
873
873
  message.params?.update?.sessionUpdate === "sources";
874
874
  if (isSourcesMessage) {
875
- // Use console.warn directly for gui-console capture
876
- console.warn("🟢 RECEIVED SOURCES SSE MESSAGE", {
877
- sourcesCount: message.params?.update?.sources?.length,
878
- isInReplayMode: this.isInReplayMode,
879
- callbackCount: this.sessionUpdateCallbacks.size,
880
- });
881
875
  // Handle sources directly without ACP schema validation
882
876
  try {
883
877
  this.handleSessionNotification(message.params);
884
- console.warn("🟢 AFTER handleSessionNotification for sources");
885
878
  }
886
879
  catch (error) {
887
- console.error("🔴 ERROR in handleSessionNotification for sources", error);
880
+ logger.error("Error in handleSessionNotification for sources", {
881
+ error,
882
+ });
888
883
  }
889
884
  return;
890
885
  }
@@ -931,9 +926,6 @@ export class HttpTransport {
931
926
  logger.debug("Skipping session notification - stream complete/cancelled");
932
927
  return;
933
928
  }
934
- if (this.streamComplete && isSourcesNotification) {
935
- console.warn("🟢 Processing sources notification after stream complete");
936
- }
937
929
  logger.debug("handleSessionNotification called", { params });
938
930
  // Extract content from the update
939
931
  const paramsExtended = params;
@@ -1548,13 +1540,6 @@ export class HttpTransport {
1548
1540
  update.sessionUpdate === "sources") {
1549
1541
  // Sources notification - citation sources from tool calls
1550
1542
  const sourcesUpdate = update;
1551
- console.warn("🔵 SOURCES in handleSessionNotification", {
1552
- sourcesCount: sourcesUpdate.sources.length,
1553
- isInReplayMode: this.isInReplayMode,
1554
- callbackCount: this.sessionUpdateCallbacks.size,
1555
- firstSourceId: sourcesUpdate.sources[0]?.id,
1556
- firstSourceToolCallId: sourcesUpdate.sources[0]?.toolCallId,
1557
- });
1558
1543
  // Create a sources session update
1559
1544
  const sessionUpdate = {
1560
1545
  type: "sources",
@@ -1567,20 +1552,9 @@ export class HttpTransport {
1567
1552
  // If no callbacks are registered yet (React hooks haven't subscribed),
1568
1553
  // queue the sources to be replayed when they do subscribe
1569
1554
  if (this.sessionUpdateCallbacks.size === 0) {
1570
- console.warn("🔵 QUEUEING sources for late-subscribing callbacks", {
1571
- sourcesCount: sourcesUpdate.sources.length,
1572
- queueLengthBefore: this.pendingReplayUpdates.length,
1573
- });
1574
1555
  this.pendingReplayUpdates.push(sessionUpdate);
1575
- console.warn("🔵 Queue length after:", {
1576
- queueLengthAfter: this.pendingReplayUpdates.length,
1577
- });
1578
1556
  }
1579
1557
  else {
1580
- console.warn("🔵 NOTIFYING sources immediately (callbacks registered)", {
1581
- sourcesCount: sourcesUpdate.sources.length,
1582
- callbackCount: this.sessionUpdateCallbacks.size,
1583
- });
1584
1558
  this.notifySessionUpdate(sessionUpdate);
1585
1559
  }
1586
1560
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@townco/ui",
3
- "version": "0.1.110",
3
+ "version": "0.1.112",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -49,7 +49,7 @@
49
49
  "@radix-ui/react-slot": "^1.2.4",
50
50
  "@radix-ui/react-tabs": "^1.1.13",
51
51
  "@radix-ui/react-tooltip": "^1.2.8",
52
- "@townco/core": "0.0.88",
52
+ "@townco/core": "0.0.90",
53
53
  "@types/mdast": "^4.0.4",
54
54
  "@uiw/react-json-view": "^2.0.0-alpha.39",
55
55
  "class-variance-authority": "^0.7.1",
@@ -67,7 +67,7 @@
67
67
  "zustand": "^5.0.8"
68
68
  },
69
69
  "devDependencies": {
70
- "@townco/tsconfig": "0.1.107",
70
+ "@townco/tsconfig": "0.1.109",
71
71
  "@types/node": "^24.10.0",
72
72
  "@types/react": "^19.2.2",
73
73
  "@types/unist": "^3.0.3",