@townco/agent 0.1.72 → 0.1.73
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/acp-server/adapter.js +54 -0
- package/dist/acp-server/http.js +42 -1
- package/dist/acp-server/session-storage.d.ts +40 -0
- package/dist/acp-server/session-storage.js +26 -0
- package/dist/runner/langchain/index.js +30 -2
- package/dist/runner/langchain/tools/subagent-connections.d.ts +34 -0
- package/dist/runner/langchain/tools/subagent-connections.js +32 -1
- package/dist/runner/langchain/tools/subagent.js +66 -8
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
|
@@ -361,8 +361,27 @@ export class AgentAcpAdapter {
|
|
|
361
361
|
...(block.icon ? { icon: block.icon } : {}),
|
|
362
362
|
...(block.subline ? { subline: block.subline } : {}),
|
|
363
363
|
...(block.batchId ? { batchId: block.batchId } : {}),
|
|
364
|
+
// Include subagent data for replay - full content is sent via direct SSE
|
|
365
|
+
// bypassing PostgreSQL NOTIFY size limits
|
|
366
|
+
...(block.subagentPort ? { subagentPort: block.subagentPort } : {}),
|
|
367
|
+
...(block.subagentSessionId
|
|
368
|
+
? { subagentSessionId: block.subagentSessionId }
|
|
369
|
+
: {}),
|
|
370
|
+
...(block.subagentMessages
|
|
371
|
+
? { subagentMessages: block.subagentMessages }
|
|
372
|
+
: {}),
|
|
364
373
|
...block._meta,
|
|
365
374
|
};
|
|
375
|
+
// Debug: log subagent data being replayed
|
|
376
|
+
logger.info("Replaying tool_call", {
|
|
377
|
+
toolCallId: block.id,
|
|
378
|
+
title: block.title,
|
|
379
|
+
batchId: block.batchId,
|
|
380
|
+
hasSubagentPort: !!block.subagentPort,
|
|
381
|
+
hasSubagentSessionId: !!block.subagentSessionId,
|
|
382
|
+
hasSubagentMessages: !!block.subagentMessages,
|
|
383
|
+
subagentMessagesCount: block.subagentMessages?.length,
|
|
384
|
+
});
|
|
366
385
|
this.connection.sessionUpdate({
|
|
367
386
|
sessionId: params.sessionId,
|
|
368
387
|
update: {
|
|
@@ -783,6 +802,41 @@ export class AgentAcpAdapter {
|
|
|
783
802
|
toolCallBlock.status === "failed") {
|
|
784
803
|
toolCallBlock.completedAt = Date.now();
|
|
785
804
|
}
|
|
805
|
+
const meta = updateMsg._meta;
|
|
806
|
+
// Update batchId from _meta (comes from tool_call_update after preliminary tool_call)
|
|
807
|
+
if (meta?.batchId && !toolCallBlock.batchId) {
|
|
808
|
+
toolCallBlock.batchId = meta.batchId;
|
|
809
|
+
}
|
|
810
|
+
if (meta?.subagentPort) {
|
|
811
|
+
toolCallBlock.subagentPort = meta.subagentPort;
|
|
812
|
+
}
|
|
813
|
+
if (meta?.subagentSessionId) {
|
|
814
|
+
toolCallBlock.subagentSessionId = meta.subagentSessionId;
|
|
815
|
+
}
|
|
816
|
+
if (meta?.subagentMessages) {
|
|
817
|
+
logger.info("Storing subagent messages for session replay", {
|
|
818
|
+
toolCallId: updateMsg.toolCallId,
|
|
819
|
+
messageCount: meta.subagentMessages.length,
|
|
820
|
+
});
|
|
821
|
+
toolCallBlock.subagentMessages = meta.subagentMessages;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
// Forward tool_call_update with _meta to the client (for subagent connection info, etc.)
|
|
825
|
+
if (updateMsg._meta) {
|
|
826
|
+
logger.info("Forwarding tool_call_update with _meta to client", {
|
|
827
|
+
toolCallId: updateMsg.toolCallId,
|
|
828
|
+
status: updateMsg.status,
|
|
829
|
+
_meta: updateMsg._meta,
|
|
830
|
+
});
|
|
831
|
+
this.connection.sessionUpdate({
|
|
832
|
+
sessionId: params.sessionId,
|
|
833
|
+
update: {
|
|
834
|
+
sessionUpdate: "tool_call_update",
|
|
835
|
+
toolCallId: updateMsg.toolCallId,
|
|
836
|
+
status: updateMsg.status,
|
|
837
|
+
_meta: updateMsg._meta,
|
|
838
|
+
},
|
|
839
|
+
});
|
|
786
840
|
}
|
|
787
841
|
// Forward tool_call_update with _meta to the client (for subagent connection info, etc.)
|
|
788
842
|
if (updateMsg._meta) {
|
package/dist/acp-server/http.js
CHANGED
|
@@ -227,6 +227,48 @@ export function makeHttpTransport(agent, agentDir, agentName) {
|
|
|
227
227
|
}
|
|
228
228
|
continue;
|
|
229
229
|
}
|
|
230
|
+
// Check if this is a tool_call with subagentMessages - send directly via SSE
|
|
231
|
+
// to bypass PostgreSQL NOTIFY size limits (7500 bytes)
|
|
232
|
+
if (messageType === "session/update" &&
|
|
233
|
+
"params" in rawMsg &&
|
|
234
|
+
rawMsg.params != null &&
|
|
235
|
+
typeof rawMsg.params === "object" &&
|
|
236
|
+
"update" in rawMsg.params &&
|
|
237
|
+
rawMsg.params.update != null &&
|
|
238
|
+
typeof rawMsg.params.update === "object" &&
|
|
239
|
+
"sessionUpdate" in rawMsg.params.update &&
|
|
240
|
+
rawMsg.params.update.sessionUpdate === "tool_call" &&
|
|
241
|
+
"_meta" in rawMsg.params.update &&
|
|
242
|
+
rawMsg.params.update._meta != null &&
|
|
243
|
+
typeof rawMsg.params.update._meta === "object" &&
|
|
244
|
+
"subagentMessages" in rawMsg.params.update._meta) {
|
|
245
|
+
// Send subagent tool call directly via SSE, bypassing PostgreSQL NOTIFY
|
|
246
|
+
const stream = sseStreams.get(sessionId);
|
|
247
|
+
if (stream) {
|
|
248
|
+
try {
|
|
249
|
+
await stream.writeSSE({
|
|
250
|
+
event: "message",
|
|
251
|
+
data: JSON.stringify(rawMsg),
|
|
252
|
+
});
|
|
253
|
+
logger.debug("Sent subagent tool call directly via SSE", {
|
|
254
|
+
sessionId,
|
|
255
|
+
payloadSize: JSON.stringify(rawMsg).length,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
logger.error("Failed to send subagent tool call", {
|
|
260
|
+
error,
|
|
261
|
+
sessionId,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
logger.warn("No SSE stream found for subagent tool call", {
|
|
267
|
+
sessionId,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
230
272
|
// Other messages (notifications, requests from agent) go to
|
|
231
273
|
// session-specific channel via PostgreSQL NOTIFY
|
|
232
274
|
const channel = safeChannelName("notifications", sessionId);
|
|
@@ -553,7 +595,6 @@ export function makeHttpTransport(agent, agentDir, agentName) {
|
|
|
553
595
|
logger.info("Starting HTTP server", { port });
|
|
554
596
|
Bun.serve({
|
|
555
597
|
fetch: app.fetch,
|
|
556
|
-
hostname: Bun.env.BIND_HOST || "localhost",
|
|
557
598
|
port,
|
|
558
599
|
});
|
|
559
600
|
logger.info("HTTP server listening", {
|
|
@@ -16,6 +16,40 @@ export interface ImageBlock {
|
|
|
16
16
|
data?: string | undefined;
|
|
17
17
|
mimeType?: string | undefined;
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Sub-agent tool call stored within a parent tool call's subagentMessages
|
|
21
|
+
*/
|
|
22
|
+
export interface SubagentToolCallBlock {
|
|
23
|
+
id: string;
|
|
24
|
+
title: string;
|
|
25
|
+
prettyName?: string | undefined;
|
|
26
|
+
icon?: string | undefined;
|
|
27
|
+
status: "pending" | "in_progress" | "completed" | "failed";
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Content block for sub-agent messages - either text or a tool call
|
|
31
|
+
*/
|
|
32
|
+
export interface SubagentTextBlock {
|
|
33
|
+
type: "text";
|
|
34
|
+
text: string;
|
|
35
|
+
}
|
|
36
|
+
export interface SubagentToolCallContentBlock {
|
|
37
|
+
type: "tool_call";
|
|
38
|
+
toolCall: SubagentToolCallBlock;
|
|
39
|
+
}
|
|
40
|
+
export type SubagentContentBlock = SubagentTextBlock | SubagentToolCallContentBlock;
|
|
41
|
+
/**
|
|
42
|
+
* Sub-agent message stored for replay
|
|
43
|
+
*/
|
|
44
|
+
export interface SubagentMessage {
|
|
45
|
+
id: string;
|
|
46
|
+
/** Accumulated text content (thinking) */
|
|
47
|
+
content: string;
|
|
48
|
+
/** Interleaved content blocks in arrival order */
|
|
49
|
+
contentBlocks?: SubagentContentBlock[] | undefined;
|
|
50
|
+
/** Tool calls made by the sub-agent */
|
|
51
|
+
toolCalls?: SubagentToolCallBlock[] | undefined;
|
|
52
|
+
}
|
|
19
53
|
export interface ToolCallBlock {
|
|
20
54
|
type: "tool_call";
|
|
21
55
|
id: string;
|
|
@@ -37,6 +71,12 @@ export interface ToolCallBlock {
|
|
|
37
71
|
originalTokens?: number;
|
|
38
72
|
finalTokens?: number;
|
|
39
73
|
};
|
|
74
|
+
/** Sub-agent HTTP port (for reference, not used in replay) */
|
|
75
|
+
subagentPort?: number | undefined;
|
|
76
|
+
/** Sub-agent session ID (for reference, not used in replay) */
|
|
77
|
+
subagentSessionId?: string | undefined;
|
|
78
|
+
/** Stored sub-agent messages for replay */
|
|
79
|
+
subagentMessages?: SubagentMessage[] | undefined;
|
|
40
80
|
}
|
|
41
81
|
export type ContentBlock = TextBlock | ImageBlock | ToolCallBlock;
|
|
42
82
|
/**
|
|
@@ -26,6 +26,29 @@ const imageBlockSchema = z.object({
|
|
|
26
26
|
data: z.string().optional(),
|
|
27
27
|
mimeType: z.string().optional(),
|
|
28
28
|
});
|
|
29
|
+
const subagentToolCallBlockSchema = z.object({
|
|
30
|
+
id: z.string(),
|
|
31
|
+
title: z.string(),
|
|
32
|
+
prettyName: z.string().optional(),
|
|
33
|
+
icon: z.string().optional(),
|
|
34
|
+
status: z.enum(["pending", "in_progress", "completed", "failed"]),
|
|
35
|
+
});
|
|
36
|
+
const subagentContentBlockSchema = z.discriminatedUnion("type", [
|
|
37
|
+
z.object({
|
|
38
|
+
type: z.literal("text"),
|
|
39
|
+
text: z.string(),
|
|
40
|
+
}),
|
|
41
|
+
z.object({
|
|
42
|
+
type: z.literal("tool_call"),
|
|
43
|
+
toolCall: subagentToolCallBlockSchema,
|
|
44
|
+
}),
|
|
45
|
+
]);
|
|
46
|
+
const subagentMessageSchema = z.object({
|
|
47
|
+
id: z.string(),
|
|
48
|
+
content: z.string(),
|
|
49
|
+
contentBlocks: z.array(subagentContentBlockSchema).optional(),
|
|
50
|
+
toolCalls: z.array(subagentToolCallBlockSchema).optional(),
|
|
51
|
+
});
|
|
29
52
|
const toolCallBlockSchema = z.object({
|
|
30
53
|
type: z.literal("tool_call"),
|
|
31
54
|
id: z.string(),
|
|
@@ -52,6 +75,9 @@ const toolCallBlockSchema = z.object({
|
|
|
52
75
|
error: z.string().optional(),
|
|
53
76
|
startedAt: z.number().optional(),
|
|
54
77
|
completedAt: z.number().optional(),
|
|
78
|
+
subagentPort: z.number().optional(),
|
|
79
|
+
subagentSessionId: z.string().optional(),
|
|
80
|
+
subagentMessages: z.array(subagentMessageSchema).optional(),
|
|
55
81
|
});
|
|
56
82
|
const contentBlockSchema = z.discriminatedUnion("type", [
|
|
57
83
|
textBlockSchema,
|
|
@@ -102,6 +102,7 @@ export class LangchainAgent {
|
|
|
102
102
|
});
|
|
103
103
|
const subagentUpdateQueue = [];
|
|
104
104
|
let subagentUpdateResolver = null;
|
|
105
|
+
const subagentMessagesQueue = [];
|
|
105
106
|
// Listen for subagent connection events - resolve any waiting promise immediately
|
|
106
107
|
const onSubagentConnection = (event) => {
|
|
107
108
|
_logger.info("Received subagent connection event", {
|
|
@@ -121,6 +122,15 @@ export class LangchainAgent {
|
|
|
121
122
|
}
|
|
122
123
|
};
|
|
123
124
|
subagentEvents.on("connection", onSubagentConnection);
|
|
125
|
+
// Listen for subagent messages events (for session storage)
|
|
126
|
+
const onSubagentMessages = (event) => {
|
|
127
|
+
_logger.info("Received subagent messages event", {
|
|
128
|
+
toolCallId: event.toolCallId,
|
|
129
|
+
messageCount: event.messages.length,
|
|
130
|
+
});
|
|
131
|
+
subagentMessagesQueue.push(event);
|
|
132
|
+
};
|
|
133
|
+
subagentEvents.on("messages", onSubagentMessages);
|
|
124
134
|
// Helper to get next subagent update (returns immediately if queued, otherwise waits)
|
|
125
135
|
const waitForSubagentUpdate = () => {
|
|
126
136
|
if (subagentUpdateQueue.length > 0) {
|
|
@@ -149,6 +159,22 @@ export class LangchainAgent {
|
|
|
149
159
|
},
|
|
150
160
|
};
|
|
151
161
|
}
|
|
162
|
+
// Also yield any pending messages updates
|
|
163
|
+
while (subagentMessagesQueue.length > 0) {
|
|
164
|
+
const messagesUpdate = subagentMessagesQueue.shift();
|
|
165
|
+
_logger.info("Yielding queued subagent messages update", {
|
|
166
|
+
toolCallId: messagesUpdate.toolCallId,
|
|
167
|
+
messageCount: messagesUpdate.messages.length,
|
|
168
|
+
});
|
|
169
|
+
yield {
|
|
170
|
+
sessionUpdate: "tool_call_update",
|
|
171
|
+
toolCallId: messagesUpdate.toolCallId,
|
|
172
|
+
_meta: {
|
|
173
|
+
messageId: req.messageId,
|
|
174
|
+
subagentMessages: messagesUpdate.messages,
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
}
|
|
152
178
|
}
|
|
153
179
|
// Start telemetry span for entire invocation
|
|
154
180
|
const invocationSpan = telemetry.startSpan("agent.invoke", {
|
|
@@ -888,8 +914,9 @@ export class LangchainAgent {
|
|
|
888
914
|
yield* yieldPendingSubagentUpdates();
|
|
889
915
|
// Now that content streaming is complete, yield all buffered tool call notifications
|
|
890
916
|
yield* flushPendingToolCalls();
|
|
891
|
-
// Clean up subagent
|
|
917
|
+
// Clean up subagent event listeners
|
|
892
918
|
subagentEvents.off("connection", onSubagentConnection);
|
|
919
|
+
subagentEvents.off("messages", onSubagentMessages);
|
|
893
920
|
// Cancel any pending wait
|
|
894
921
|
if (subagentUpdateResolver) {
|
|
895
922
|
subagentUpdateResolver = null;
|
|
@@ -907,8 +934,9 @@ export class LangchainAgent {
|
|
|
907
934
|
};
|
|
908
935
|
}
|
|
909
936
|
catch (error) {
|
|
910
|
-
// Clean up subagent
|
|
937
|
+
// Clean up subagent event listeners on error
|
|
911
938
|
subagentEvents.off("connection", onSubagentConnection);
|
|
939
|
+
subagentEvents.off("messages", onSubagentMessages);
|
|
912
940
|
// Log error and end span with error status
|
|
913
941
|
telemetry.log("error", "Agent invocation failed", {
|
|
914
942
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -7,6 +7,35 @@ export interface SubagentConnectionInfo {
|
|
|
7
7
|
port: number;
|
|
8
8
|
sessionId: string;
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Sub-agent tool call tracked during streaming
|
|
12
|
+
*/
|
|
13
|
+
export interface SubagentToolCall {
|
|
14
|
+
id: string;
|
|
15
|
+
title: string;
|
|
16
|
+
prettyName?: string | undefined;
|
|
17
|
+
icon?: string | undefined;
|
|
18
|
+
status: "pending" | "in_progress" | "completed" | "failed";
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Content block for sub-agent messages
|
|
22
|
+
*/
|
|
23
|
+
export type SubagentContentBlock = {
|
|
24
|
+
type: "text";
|
|
25
|
+
text: string;
|
|
26
|
+
} | {
|
|
27
|
+
type: "tool_call";
|
|
28
|
+
toolCall: SubagentToolCall;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Sub-agent message accumulated during streaming
|
|
32
|
+
*/
|
|
33
|
+
export interface SubagentMessage {
|
|
34
|
+
id: string;
|
|
35
|
+
content: string;
|
|
36
|
+
contentBlocks: SubagentContentBlock[];
|
|
37
|
+
toolCalls: SubagentToolCall[];
|
|
38
|
+
}
|
|
10
39
|
/**
|
|
11
40
|
* Event emitter for subagent connection events.
|
|
12
41
|
* The runner listens to these events and emits tool_call_update.
|
|
@@ -26,3 +55,8 @@ export declare function hashQuery(query: string): string;
|
|
|
26
55
|
* Emits an event that the runner can listen to.
|
|
27
56
|
*/
|
|
28
57
|
export declare function emitSubagentConnection(queryHash: string, connectionInfo: SubagentConnectionInfo): void;
|
|
58
|
+
/**
|
|
59
|
+
* Called by the subagent tool when it completes with accumulated messages.
|
|
60
|
+
* Emits an event with the messages for session storage.
|
|
61
|
+
*/
|
|
62
|
+
export declare function emitSubagentMessages(queryHash: string, messages: SubagentMessage[]): void;
|
|
@@ -12,6 +12,11 @@ export const subagentEvents = new EventEmitter();
|
|
|
12
12
|
* Set by the runner when it sees a subagent tool_call.
|
|
13
13
|
*/
|
|
14
14
|
export const queryToToolCallId = new Map();
|
|
15
|
+
/**
|
|
16
|
+
* Maps query hash to resolved toolCallId (preserved after connection event).
|
|
17
|
+
* Used to correlate messages when the tool completes.
|
|
18
|
+
*/
|
|
19
|
+
const queryToResolvedToolCallId = new Map();
|
|
15
20
|
/**
|
|
16
21
|
* Generate a hash from the query string for correlation.
|
|
17
22
|
*/
|
|
@@ -46,7 +51,8 @@ export function emitSubagentConnection(queryHash, connectionInfo) {
|
|
|
46
51
|
toolCallId,
|
|
47
52
|
...connectionInfo,
|
|
48
53
|
});
|
|
49
|
-
//
|
|
54
|
+
// Preserve the toolCallId for message emission, but remove from pending lookup
|
|
55
|
+
queryToResolvedToolCallId.set(queryHash, toolCallId);
|
|
50
56
|
queryToToolCallId.delete(queryHash);
|
|
51
57
|
}
|
|
52
58
|
else {
|
|
@@ -56,3 +62,28 @@ export function emitSubagentConnection(queryHash, connectionInfo) {
|
|
|
56
62
|
});
|
|
57
63
|
}
|
|
58
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Called by the subagent tool when it completes with accumulated messages.
|
|
67
|
+
* Emits an event with the messages for session storage.
|
|
68
|
+
*/
|
|
69
|
+
export function emitSubagentMessages(queryHash, messages) {
|
|
70
|
+
const toolCallId = queryToResolvedToolCallId.get(queryHash);
|
|
71
|
+
if (toolCallId) {
|
|
72
|
+
logger.info("Emitting subagent messages for storage", {
|
|
73
|
+
queryHash,
|
|
74
|
+
toolCallId,
|
|
75
|
+
messageCount: messages.length,
|
|
76
|
+
});
|
|
77
|
+
subagentEvents.emit("messages", {
|
|
78
|
+
toolCallId,
|
|
79
|
+
messages,
|
|
80
|
+
});
|
|
81
|
+
// Clean up the resolved mapping
|
|
82
|
+
queryToResolvedToolCallId.delete(queryHash);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
logger.warn("No resolved toolCallId for messages emission", {
|
|
86
|
+
queryHash,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -7,7 +7,7 @@ import { createLogger as coreCreateLogger } from "@townco/core";
|
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
import { SUBAGENT_MODE_KEY } from "../../../acp-server/adapter.js";
|
|
9
9
|
import { findAvailablePort } from "./port-utils.js";
|
|
10
|
-
import { emitSubagentConnection, hashQuery } from "./subagent-connections.js";
|
|
10
|
+
import { emitSubagentConnection, emitSubagentMessages, hashQuery, } from "./subagent-connections.js";
|
|
11
11
|
/**
|
|
12
12
|
* Name of the Task tool created by makeSubagentsTool
|
|
13
13
|
*/
|
|
@@ -317,6 +317,15 @@ async function querySubagent(agentName, agentPath, agentWorkingDirectory, query)
|
|
|
317
317
|
// Step 3: Connect to SSE for receiving streaming responses
|
|
318
318
|
sseAbortController = new AbortController();
|
|
319
319
|
let responseText = "";
|
|
320
|
+
// Track full message structure for session storage
|
|
321
|
+
const currentMessage = {
|
|
322
|
+
id: `subagent-${Date.now()}`,
|
|
323
|
+
content: "",
|
|
324
|
+
contentBlocks: [],
|
|
325
|
+
toolCalls: [],
|
|
326
|
+
};
|
|
327
|
+
// Map of tool call IDs to their indices in toolCalls array
|
|
328
|
+
const toolCallMap = new Map();
|
|
320
329
|
const ssePromise = (async () => {
|
|
321
330
|
const sseResponse = await fetch(`${baseUrl}/events`, {
|
|
322
331
|
headers: { "X-Session-ID": sessionId },
|
|
@@ -342,18 +351,63 @@ async function querySubagent(agentName, agentPath, agentWorkingDirectory, query)
|
|
|
342
351
|
continue;
|
|
343
352
|
try {
|
|
344
353
|
const message = JSON.parse(data);
|
|
345
|
-
|
|
346
|
-
if (message.method
|
|
347
|
-
|
|
348
|
-
|
|
354
|
+
const update = message.params?.update;
|
|
355
|
+
if (message.method !== "session/update" || !update)
|
|
356
|
+
continue;
|
|
357
|
+
// Handle agent_message_chunk - accumulate text
|
|
358
|
+
if (update.sessionUpdate === "agent_message_chunk") {
|
|
359
|
+
const content = update.content;
|
|
349
360
|
if (content?.type === "text" &&
|
|
350
361
|
typeof content.text === "string") {
|
|
351
362
|
responseText += content.text;
|
|
363
|
+
currentMessage.content += content.text;
|
|
364
|
+
// Add to contentBlocks - append to last text block or create new one
|
|
365
|
+
const lastBlock = currentMessage.contentBlocks[currentMessage.contentBlocks.length - 1];
|
|
366
|
+
if (lastBlock && lastBlock.type === "text") {
|
|
367
|
+
lastBlock.text += content.text;
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
currentMessage.contentBlocks.push({
|
|
371
|
+
type: "text",
|
|
372
|
+
text: content.text,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
352
375
|
}
|
|
353
376
|
}
|
|
354
|
-
//
|
|
355
|
-
if (
|
|
356
|
-
|
|
377
|
+
// Handle tool_call - track new tool calls
|
|
378
|
+
if (update.sessionUpdate === "tool_call" && update.toolCallId) {
|
|
379
|
+
const toolCall = {
|
|
380
|
+
id: update.toolCallId,
|
|
381
|
+
title: update.title || "Tool call",
|
|
382
|
+
prettyName: update._meta?.prettyName,
|
|
383
|
+
icon: update._meta?.icon,
|
|
384
|
+
status: update.status || "pending",
|
|
385
|
+
};
|
|
386
|
+
currentMessage.toolCalls.push(toolCall);
|
|
387
|
+
toolCallMap.set(update.toolCallId, currentMessage.toolCalls.length - 1);
|
|
388
|
+
// Add to contentBlocks for interleaved display
|
|
389
|
+
currentMessage.contentBlocks.push({
|
|
390
|
+
type: "tool_call",
|
|
391
|
+
toolCall,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
// Handle tool_call_update - update existing tool call status
|
|
395
|
+
if (update.sessionUpdate === "tool_call_update" &&
|
|
396
|
+
update.toolCallId) {
|
|
397
|
+
const idx = toolCallMap.get(update.toolCallId);
|
|
398
|
+
if (idx !== undefined && currentMessage.toolCalls[idx]) {
|
|
399
|
+
if (update.status) {
|
|
400
|
+
currentMessage.toolCalls[idx].status =
|
|
401
|
+
update.status;
|
|
402
|
+
}
|
|
403
|
+
// Also update in contentBlocks
|
|
404
|
+
const block = currentMessage.contentBlocks.find((b) => b.type === "tool_call" &&
|
|
405
|
+
b.toolCall.id === update.toolCallId);
|
|
406
|
+
if (block && update.status) {
|
|
407
|
+
block.toolCall.status =
|
|
408
|
+
update.status;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
357
411
|
}
|
|
358
412
|
}
|
|
359
413
|
catch {
|
|
@@ -404,6 +458,10 @@ async function querySubagent(agentName, agentPath, agentWorkingDirectory, query)
|
|
|
404
458
|
ssePromise.catch(() => { }), // Ignore abort errors
|
|
405
459
|
new Promise((r) => setTimeout(r, 1000)),
|
|
406
460
|
]);
|
|
461
|
+
// Emit accumulated messages for session storage
|
|
462
|
+
if (currentMessage.content || currentMessage.toolCalls.length > 0) {
|
|
463
|
+
emitSubagentMessages(queryHash, [currentMessage]);
|
|
464
|
+
}
|
|
407
465
|
return responseText;
|
|
408
466
|
}
|
|
409
467
|
finally {
|