@sumicom/quicksave 0.8.9 → 0.8.10
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/ai/cardBuilder.d.ts +62 -2
- package/dist/ai/cardBuilder.d.ts.map +1 -1
- package/dist/ai/cardBuilder.js +331 -25
- package/dist/ai/cardBuilder.js.map +1 -1
- package/dist/ai/claudeCliProvider.d.ts +14 -0
- package/dist/ai/claudeCliProvider.d.ts.map +1 -1
- package/dist/ai/claudeCliProvider.js +83 -2
- package/dist/ai/claudeCliProvider.js.map +1 -1
- package/dist/ai/claudeCodeProvider.d.ts.map +1 -1
- package/dist/ai/claudeCodeProvider.js +2 -0
- package/dist/ai/claudeCodeProvider.js.map +1 -1
- package/dist/ai/claudeSdkProvider.d.ts.map +1 -1
- package/dist/ai/claudeSdkProvider.js +13 -2
- package/dist/ai/claudeSdkProvider.js.map +1 -1
- package/dist/ai/claudeTerminal/cardSynth.d.ts +63 -0
- package/dist/ai/claudeTerminal/cardSynth.d.ts.map +1 -0
- package/dist/ai/claudeTerminal/cardSynth.js +92 -0
- package/dist/ai/claudeTerminal/cardSynth.js.map +1 -0
- package/dist/ai/claudeTerminal/hookBridge.d.ts +56 -0
- package/dist/ai/claudeTerminal/hookBridge.d.ts.map +1 -0
- package/dist/ai/claudeTerminal/hookBridge.js +131 -0
- package/dist/ai/claudeTerminal/hookBridge.js.map +1 -0
- package/dist/ai/claudeTerminal/hookHandler.d.ts +18 -0
- package/dist/ai/claudeTerminal/hookHandler.d.ts.map +1 -0
- package/dist/ai/claudeTerminal/hookHandler.js +124 -0
- package/dist/ai/claudeTerminal/hookHandler.js.map +1 -0
- package/dist/ai/claudeTerminal/index.d.ts +5 -0
- package/dist/ai/claudeTerminal/index.d.ts.map +1 -0
- package/dist/ai/claudeTerminal/index.js +7 -0
- package/dist/ai/claudeTerminal/index.js.map +1 -0
- package/dist/ai/claudeTerminal/jsonlTail.d.ts +40 -0
- package/dist/ai/claudeTerminal/jsonlTail.d.ts.map +1 -0
- package/dist/ai/claudeTerminal/jsonlTail.js +129 -0
- package/dist/ai/claudeTerminal/jsonlTail.js.map +1 -0
- package/dist/ai/claudeTerminal/provider.d.ts +106 -0
- package/dist/ai/claudeTerminal/provider.d.ts.map +1 -0
- package/dist/ai/claudeTerminal/provider.js +579 -0
- package/dist/ai/claudeTerminal/provider.js.map +1 -0
- package/dist/ai/claudeTerminal/settingsBuilder.d.ts +44 -0
- package/dist/ai/claudeTerminal/settingsBuilder.d.ts.map +1 -0
- package/dist/ai/claudeTerminal/settingsBuilder.js +40 -0
- package/dist/ai/claudeTerminal/settingsBuilder.js.map +1 -0
- package/dist/ai/codexAppServer/cardAdapter.d.ts.map +1 -1
- package/dist/ai/codexAppServer/cardAdapter.js +13 -5
- package/dist/ai/codexAppServer/cardAdapter.js.map +1 -1
- package/dist/ai/codexAppServer/provider.d.ts +9 -2
- package/dist/ai/codexAppServer/provider.d.ts.map +1 -1
- package/dist/ai/codexAppServer/provider.js +157 -22
- package/dist/ai/codexAppServer/provider.js.map +1 -1
- package/dist/ai/profiles/project-sandbox.sb +26 -0
- package/dist/ai/provider.d.ts +29 -1
- package/dist/ai/provider.d.ts.map +1 -1
- package/dist/ai/provider.js +1 -0
- package/dist/ai/provider.js.map +1 -1
- package/dist/ai/queuedUserPrompts.d.ts +8 -0
- package/dist/ai/queuedUserPrompts.d.ts.map +1 -0
- package/dist/ai/queuedUserPrompts.js +19 -0
- package/dist/ai/queuedUserPrompts.js.map +1 -0
- package/dist/ai/realtimeTranscription.d.ts +39 -0
- package/dist/ai/realtimeTranscription.d.ts.map +1 -0
- package/dist/ai/realtimeTranscription.js +154 -0
- package/dist/ai/realtimeTranscription.js.map +1 -0
- package/dist/ai/sandboxMcp.d.ts +7 -0
- package/dist/ai/sandboxMcp.d.ts.map +1 -1
- package/dist/ai/sandboxMcp.js +4 -0
- package/dist/ai/sandboxMcp.js.map +1 -1
- package/dist/ai/sandboxMcpStdio.d.ts +1 -1
- package/dist/ai/sandboxMcpStdio.js +120 -47
- package/dist/ai/sandboxMcpStdio.js.map +1 -1
- package/dist/ai/sessionManager.d.ts +14 -0
- package/dist/ai/sessionManager.d.ts.map +1 -1
- package/dist/ai/sessionManager.js +144 -3
- package/dist/ai/sessionManager.js.map +1 -1
- package/dist/ai/sessionRegistry.d.ts +6 -0
- package/dist/ai/sessionRegistry.d.ts.map +1 -1
- package/dist/ai/sessionRegistry.js +13 -0
- package/dist/ai/sessionRegistry.js.map +1 -1
- package/dist/ai/sessionRegistryLocator.d.ts +14 -0
- package/dist/ai/sessionRegistryLocator.d.ts.map +1 -0
- package/dist/ai/sessionRegistryLocator.js +44 -0
- package/dist/ai/sessionRegistryLocator.js.map +1 -0
- package/dist/ai/systemPrompt.d.ts.map +1 -1
- package/dist/ai/systemPrompt.js +1 -0
- package/dist/ai/systemPrompt.js.map +1 -1
- package/dist/ai/voiceStream.d.ts +74 -0
- package/dist/ai/voiceStream.d.ts.map +1 -0
- package/dist/ai/voiceStream.js +179 -0
- package/dist/ai/voiceStream.js.map +1 -0
- package/dist/ai/voiceTranscription.d.ts +33 -0
- package/dist/ai/voiceTranscription.d.ts.map +1 -0
- package/dist/ai/voiceTranscription.js +122 -0
- package/dist/ai/voiceTranscription.js.map +1 -0
- package/dist/files/fileBrowser.d.ts.map +1 -1
- package/dist/files/fileBrowser.js +3 -2
- package/dist/files/fileBrowser.js.map +1 -1
- package/dist/handlers/legacyBusAdapter.d.ts +1 -1
- package/dist/handlers/legacyBusAdapter.d.ts.map +1 -1
- package/dist/handlers/legacyBusAdapter.js +6 -1
- package/dist/handlers/legacyBusAdapter.js.map +1 -1
- package/dist/handlers/messageHandler.d.ts +5 -0
- package/dist/handlers/messageHandler.d.ts.map +1 -1
- package/dist/handlers/messageHandler.js +100 -5
- package/dist/handlers/messageHandler.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/service/ipcClient.js +1 -1
- package/dist/service/ipcClient.js.map +1 -1
- package/dist/service/run.d.ts.map +1 -1
- package/dist/service/run.js +74 -3
- package/dist/service/run.js.map +1 -1
- package/dist/service/types.js +1 -1
- package/package.json +6 -3
package/dist/ai/cardBuilder.d.ts
CHANGED
|
@@ -1,9 +1,31 @@
|
|
|
1
|
-
import type { Attachment, Card, CardId, CardEvent, CardHistoryResponse, PendingInputAttachment } from '@sumicom/quicksave-shared';
|
|
1
|
+
import type { Attachment, Card, CardId, CardEvent, CardHistoryResponse, PendingInputAttachment, GeneratedImageCard, SystemCard } from '@sumicom/quicksave-shared';
|
|
2
2
|
/**
|
|
3
3
|
* Load persisted card history for a memory-mode session.
|
|
4
4
|
* Returns cards in insertion order, or empty array if none exist.
|
|
5
5
|
*/
|
|
6
6
|
export declare function loadPersistedCards(sessionId: string): Promise<Card[]>;
|
|
7
|
+
export declare function encodeCwdPath(cwd: string): string;
|
|
8
|
+
export declare function claudeProjectDir(cwd: string): string;
|
|
9
|
+
export declare function jsonlPath(sessionId: string, cwd: string): string;
|
|
10
|
+
/** Compact "1m 25s" / "45s" duration for the `text` fallback. The PWA renders
|
|
11
|
+
* its own badge from `meta.durationMs`; this keeps debug logs and non-PWA
|
|
12
|
+
* consumers readable. */
|
|
13
|
+
export declare function formatDurationMs(ms: number): string;
|
|
14
|
+
/** Build a structured `turn_duration` system card from a Claude JSONL entry. */
|
|
15
|
+
export declare function turnDurationCard(msg: {
|
|
16
|
+
durationMs?: unknown;
|
|
17
|
+
messageCount?: unknown;
|
|
18
|
+
}, id: CardId): SystemCard;
|
|
19
|
+
/** Build a structured `stop_hook_summary` system card from a Claude JSONL entry.
|
|
20
|
+
* Flags as `warning` when any hook errored or blocked continuation — that's the
|
|
21
|
+
* channel that surfaces hook misconfigs (e.g. a bad interpreter path). */
|
|
22
|
+
export declare function stopHookSummaryCard(msg: {
|
|
23
|
+
hookInfos?: unknown;
|
|
24
|
+
hookErrors?: unknown;
|
|
25
|
+
level?: unknown;
|
|
26
|
+
preventedContinuation?: unknown;
|
|
27
|
+
stopReason?: unknown;
|
|
28
|
+
}, id: CardId): SystemCard;
|
|
7
29
|
export declare class StreamCardBuilder {
|
|
8
30
|
private sessionId;
|
|
9
31
|
private cwd;
|
|
@@ -15,6 +37,14 @@ export declare class StreamCardBuilder {
|
|
|
15
37
|
private agentIdToCardId;
|
|
16
38
|
/** Cards created for subagent permissions — removed after resolution. */
|
|
17
39
|
private ephemeralCards;
|
|
40
|
+
/** Agent tool_use input stashed from assistant message, keyed by tool_use block.id.
|
|
41
|
+
* Consumed when task_started arrives with the matching tool_use_id. */
|
|
42
|
+
private agentToolUseStash;
|
|
43
|
+
/** Card ID of the currently running subagent; tool calls are nested here instead of
|
|
44
|
+
* being emitted as top-level ToolCallCards. Cleared on task_notification. */
|
|
45
|
+
private activeSubagentCardId;
|
|
46
|
+
/** Nested tool_use_id → subagentCardId — lets toolResult() route to the right card. */
|
|
47
|
+
private nestedToolUseToSubagentCard;
|
|
18
48
|
/** Current streaming assistant_text card (for append_text events) */
|
|
19
49
|
private currentTextCardId;
|
|
20
50
|
/** JSONL file byte offset at the start of the current turn — history reads stop here. */
|
|
@@ -34,6 +64,11 @@ export declare class StreamCardBuilder {
|
|
|
34
64
|
* Call before clearCards() at the end of each turn.
|
|
35
65
|
*/
|
|
36
66
|
persistCards(): Promise<void>;
|
|
67
|
+
/** Persist a single card immediately. Used when a memory-mode provider
|
|
68
|
+
* injects a user prompt into an already-running turn; the card must survive
|
|
69
|
+
* refresh before the turn's normal end-of-turn persist runs. */
|
|
70
|
+
persistCard(card: Card): Promise<void>;
|
|
71
|
+
private persistCardBatch;
|
|
37
72
|
/** Snapshot current JSONL byte size so history reads stop before the active turn. */
|
|
38
73
|
snapshotCutoff(): Promise<void>;
|
|
39
74
|
/**
|
|
@@ -103,10 +138,35 @@ export declare class StreamCardBuilder {
|
|
|
103
138
|
toolResult(toolUseId: string, content: string, isError: boolean): CardEvent | null;
|
|
104
139
|
/** Attach AskUserQuestion answers to the matching ToolCallCard. */
|
|
105
140
|
setToolAnswers(toolUseId: string, answers: Record<string, string>): CardEvent | null;
|
|
106
|
-
|
|
141
|
+
/** Stash Agent tool_use input fields for later use when task_started arrives. */
|
|
142
|
+
stashAgentToolUseInput(toolUseId: string, input: {
|
|
143
|
+
prompt?: string;
|
|
144
|
+
subagentType?: string;
|
|
145
|
+
requestedModel?: string;
|
|
146
|
+
}): void;
|
|
147
|
+
subagentStart(description: string, agentId: string, toolUseId?: string, extras?: {
|
|
148
|
+
prompt?: string;
|
|
149
|
+
subagentType?: string;
|
|
150
|
+
requestedModel?: string;
|
|
151
|
+
}): CardEvent;
|
|
107
152
|
subagentProgress(agentId: string, toolUseId: string | undefined, toolUseCount?: number, lastToolName?: string): CardEvent | null;
|
|
108
153
|
subagentEnd(agentId: string, toolUseId: string | undefined, status: 'completed' | 'failed' | 'stopped', summary?: string): CardEvent | null;
|
|
109
154
|
systemMessage(text: string, subtype?: 'compacted' | 'cost' | 'error' | 'info' | 'warning'): CardEvent;
|
|
155
|
+
/** Live structured `turn_duration` card — mirrors the history builder so a
|
|
156
|
+
* reload renders identically. */
|
|
157
|
+
turnDuration(raw: {
|
|
158
|
+
durationMs?: unknown;
|
|
159
|
+
messageCount?: unknown;
|
|
160
|
+
}): CardEvent;
|
|
161
|
+
/** Live structured `stop_hook_summary` card — see history builder counterpart. */
|
|
162
|
+
stopHookSummary(raw: {
|
|
163
|
+
hookInfos?: unknown;
|
|
164
|
+
hookErrors?: unknown;
|
|
165
|
+
level?: unknown;
|
|
166
|
+
preventedContinuation?: unknown;
|
|
167
|
+
stopReason?: unknown;
|
|
168
|
+
}): CardEvent;
|
|
169
|
+
generatedImage(prompt: string, status: GeneratedImageCard['status'], savedPath?: string): CardEvent;
|
|
110
170
|
recoverySuggested(reason: string, action: 'compact', label: string): CardEvent;
|
|
111
171
|
errorMessage(text: string): CardEvent;
|
|
112
172
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cardBuilder.d.ts","sourceRoot":"","sources":["../../src/ai/cardBuilder.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,UAAU,EAEV,IAAI,EACJ,MAAM,EACN,SAAS,EAKT,mBAAmB,
|
|
1
|
+
{"version":3,"file":"cardBuilder.d.ts","sourceRoot":"","sources":["../../src/ai/cardBuilder.ts"],"names":[],"mappings":"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"}
|
package/dist/ai/cardBuilder.js
CHANGED
|
@@ -46,6 +46,22 @@ const TOOL_RESULT_TRUNCATE_LENGTH = 500;
|
|
|
46
46
|
function cardHistoryPath(sessionId) {
|
|
47
47
|
return join(getCardHistoryDir(), `${sessionId}.json`);
|
|
48
48
|
}
|
|
49
|
+
function cleanPersistedCard(card) {
|
|
50
|
+
const { pendingInput, ...rest } = card;
|
|
51
|
+
void pendingInput;
|
|
52
|
+
if (rest.type === 'assistant_text') {
|
|
53
|
+
return { ...rest, streaming: false };
|
|
54
|
+
}
|
|
55
|
+
return rest;
|
|
56
|
+
}
|
|
57
|
+
function mergeCardsById(existing, incoming) {
|
|
58
|
+
const byId = new Map();
|
|
59
|
+
for (const card of existing)
|
|
60
|
+
byId.set(card.id, card);
|
|
61
|
+
for (const card of incoming)
|
|
62
|
+
byId.set(card.id, card);
|
|
63
|
+
return Array.from(byId.values());
|
|
64
|
+
}
|
|
49
65
|
/**
|
|
50
66
|
* Load persisted card history for a memory-mode session.
|
|
51
67
|
* Returns cards in insertion order, or empty array if none exist.
|
|
@@ -64,13 +80,13 @@ export async function loadPersistedCards(sessionId) {
|
|
|
64
80
|
}
|
|
65
81
|
}
|
|
66
82
|
// ── Direct JSONL file reading (replaces SDK getSessionMessages/listSubagents) ──
|
|
67
|
-
function encodeCwdPath(cwd) {
|
|
83
|
+
export function encodeCwdPath(cwd) {
|
|
68
84
|
return cwd.replace(/\//g, '-');
|
|
69
85
|
}
|
|
70
|
-
function claudeProjectDir(cwd) {
|
|
86
|
+
export function claudeProjectDir(cwd) {
|
|
71
87
|
return join(homedir(), '.claude', 'projects', encodeCwdPath(cwd));
|
|
72
88
|
}
|
|
73
|
-
function jsonlPath(sessionId, cwd) {
|
|
89
|
+
export function jsonlPath(sessionId, cwd) {
|
|
74
90
|
return join(claudeProjectDir(cwd), sessionId + '.jsonl');
|
|
75
91
|
}
|
|
76
92
|
function parseJSONLContent(content) {
|
|
@@ -220,6 +236,58 @@ function extractToolResultText(content) {
|
|
|
220
236
|
}
|
|
221
237
|
return JSON.stringify(content);
|
|
222
238
|
}
|
|
239
|
+
/** Compact "1m 25s" / "45s" duration for the `text` fallback. The PWA renders
|
|
240
|
+
* its own badge from `meta.durationMs`; this keeps debug logs and non-PWA
|
|
241
|
+
* consumers readable. */
|
|
242
|
+
export function formatDurationMs(ms) {
|
|
243
|
+
if (!Number.isFinite(ms) || ms < 0)
|
|
244
|
+
return '0s';
|
|
245
|
+
const totalSec = Math.round(ms / 1000);
|
|
246
|
+
const m = Math.floor(totalSec / 60);
|
|
247
|
+
const s = totalSec % 60;
|
|
248
|
+
return m > 0 ? `${m}m ${s}s` : `${s}s`;
|
|
249
|
+
}
|
|
250
|
+
/** Build a structured `turn_duration` system card from a Claude JSONL entry. */
|
|
251
|
+
export function turnDurationCard(msg, id) {
|
|
252
|
+
const durationMs = typeof msg.durationMs === 'number' ? msg.durationMs : 0;
|
|
253
|
+
const messageCount = typeof msg.messageCount === 'number' ? msg.messageCount : 0;
|
|
254
|
+
return {
|
|
255
|
+
type: 'system',
|
|
256
|
+
id,
|
|
257
|
+
timestamp: Date.now(),
|
|
258
|
+
text: `Turn: ${formatDurationMs(durationMs)} · ${messageCount} messages`,
|
|
259
|
+
subtype: 'info',
|
|
260
|
+
meta: { kind: 'turn_duration', durationMs, messageCount },
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
/** Build a structured `stop_hook_summary` system card from a Claude JSONL entry.
|
|
264
|
+
* Flags as `warning` when any hook errored or blocked continuation — that's the
|
|
265
|
+
* channel that surfaces hook misconfigs (e.g. a bad interpreter path). */
|
|
266
|
+
export function stopHookSummaryCard(msg, id) {
|
|
267
|
+
const hooks = Array.isArray(msg.hookInfos)
|
|
268
|
+
? msg.hookInfos.map((h) => ({
|
|
269
|
+
command: typeof h?.command === 'string' ? h.command : '',
|
|
270
|
+
durationMs: typeof h?.durationMs === 'number' ? h.durationMs : 0,
|
|
271
|
+
}))
|
|
272
|
+
: [];
|
|
273
|
+
const errors = Array.isArray(msg.hookErrors)
|
|
274
|
+
? msg.hookErrors.filter((e) => typeof e === 'string')
|
|
275
|
+
: [];
|
|
276
|
+
const preventedContinuation = msg.preventedContinuation === true;
|
|
277
|
+
const level = typeof msg.level === 'string' ? msg.level : undefined;
|
|
278
|
+
const stopReason = typeof msg.stopReason === 'string' && msg.stopReason ? msg.stopReason : undefined;
|
|
279
|
+
const text = errors.length > 0
|
|
280
|
+
? `Stop hook: ${hooks.length} ran, ${errors.length} error${errors.length > 1 ? 's' : ''}`
|
|
281
|
+
: `Stop hook: ${hooks.length} ran`;
|
|
282
|
+
return {
|
|
283
|
+
type: 'system',
|
|
284
|
+
id,
|
|
285
|
+
timestamp: Date.now(),
|
|
286
|
+
text,
|
|
287
|
+
subtype: errors.length > 0 || preventedContinuation ? 'warning' : 'info',
|
|
288
|
+
meta: { kind: 'stop_hook_summary', hooks, errors, level, preventedContinuation, stopReason },
|
|
289
|
+
};
|
|
290
|
+
}
|
|
223
291
|
// ============================================================================
|
|
224
292
|
// StreamCardBuilder — streaming session, emits CardEvents
|
|
225
293
|
// ============================================================================
|
|
@@ -234,6 +302,14 @@ export class StreamCardBuilder {
|
|
|
234
302
|
agentIdToCardId = new Map();
|
|
235
303
|
/** Cards created for subagent permissions — removed after resolution. */
|
|
236
304
|
ephemeralCards = new Set();
|
|
305
|
+
/** Agent tool_use input stashed from assistant message, keyed by tool_use block.id.
|
|
306
|
+
* Consumed when task_started arrives with the matching tool_use_id. */
|
|
307
|
+
agentToolUseStash = new Map();
|
|
308
|
+
/** Card ID of the currently running subagent; tool calls are nested here instead of
|
|
309
|
+
* being emitted as top-level ToolCallCards. Cleared on task_notification. */
|
|
310
|
+
activeSubagentCardId = null;
|
|
311
|
+
/** Nested tool_use_id → subagentCardId — lets toolResult() route to the right card. */
|
|
312
|
+
nestedToolUseToSubagentCard = new Map();
|
|
237
313
|
/** Current streaming assistant_text card (for append_text events) */
|
|
238
314
|
currentTextCardId = null;
|
|
239
315
|
/** JSONL file byte offset at the start of the current turn — history reads stop here. */
|
|
@@ -258,6 +334,9 @@ export class StreamCardBuilder {
|
|
|
258
334
|
this.toolUseIdToCardId.clear();
|
|
259
335
|
this.agentIdToCardId.clear();
|
|
260
336
|
this.ephemeralCards.clear();
|
|
337
|
+
this.agentToolUseStash.clear();
|
|
338
|
+
this.activeSubagentCardId = null;
|
|
339
|
+
this.nestedToolUseToSubagentCard.clear();
|
|
261
340
|
this.currentTextCardId = null;
|
|
262
341
|
}
|
|
263
342
|
/**
|
|
@@ -269,17 +348,20 @@ export class StreamCardBuilder {
|
|
|
269
348
|
const cards = this.getCards();
|
|
270
349
|
if (cards.length === 0)
|
|
271
350
|
return;
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
351
|
+
await this.persistCardBatch(cards);
|
|
352
|
+
}
|
|
353
|
+
/** Persist a single card immediately. Used when a memory-mode provider
|
|
354
|
+
* injects a user prompt into an already-running turn; the card must survive
|
|
355
|
+
* refresh before the turn's normal end-of-turn persist runs. */
|
|
356
|
+
async persistCard(card) {
|
|
357
|
+
await this.persistCardBatch([card]);
|
|
358
|
+
}
|
|
359
|
+
async persistCardBatch(cards) {
|
|
360
|
+
if (cards.length === 0)
|
|
361
|
+
return;
|
|
362
|
+
const cleaned = cards.map(cleanPersistedCard);
|
|
280
363
|
const dir = getCardHistoryDir();
|
|
281
364
|
const p = cardHistoryPath(this.sessionId);
|
|
282
|
-
// Append to existing history
|
|
283
365
|
let existing = [];
|
|
284
366
|
try {
|
|
285
367
|
if (existsSync(p)) {
|
|
@@ -290,7 +372,7 @@ export class StreamCardBuilder {
|
|
|
290
372
|
}
|
|
291
373
|
}
|
|
292
374
|
catch { /* start fresh */ }
|
|
293
|
-
const merged =
|
|
375
|
+
const merged = mergeCardsById(existing, cleaned);
|
|
294
376
|
await mkdir(dir, { recursive: true });
|
|
295
377
|
await writeFile(p, JSON.stringify(merged) + '\n');
|
|
296
378
|
}
|
|
@@ -364,6 +446,9 @@ export class StreamCardBuilder {
|
|
|
364
446
|
this.toolUseIdToCardId.clear();
|
|
365
447
|
this.agentIdToCardId.clear();
|
|
366
448
|
this.ephemeralCards.clear();
|
|
449
|
+
this.agentToolUseStash.clear();
|
|
450
|
+
this.activeSubagentCardId = null;
|
|
451
|
+
this.nestedToolUseToSubagentCard.clear();
|
|
367
452
|
this.currentTextCardId = null;
|
|
368
453
|
}
|
|
369
454
|
/** Cancel any pending deferred clear. Called on hot resume so the queued clear
|
|
@@ -468,6 +553,15 @@ export class StreamCardBuilder {
|
|
|
468
553
|
*/
|
|
469
554
|
toolUse(toolName, toolInput, toolUseId) {
|
|
470
555
|
this.currentTextCardId = null;
|
|
556
|
+
// Route to active subagent: nest this tool call inside the SubagentCard
|
|
557
|
+
if (this.activeSubagentCardId) {
|
|
558
|
+
const subagentCard = this.cards.get(this.activeSubagentCardId);
|
|
559
|
+
if (subagentCard) {
|
|
560
|
+
const toolCalls = [...(subagentCard.toolCalls ?? []), { id: toolUseId, toolName, toolInput }];
|
|
561
|
+
this.nestedToolUseToSubagentCard.set(toolUseId, this.activeSubagentCardId);
|
|
562
|
+
return this.updateEvent(this.activeSubagentCardId, { toolCalls });
|
|
563
|
+
}
|
|
564
|
+
}
|
|
471
565
|
// Check if card was pre-created from canUseTool
|
|
472
566
|
const existingCardId = this.toolUseIdToCardId.get(toolUseId);
|
|
473
567
|
if (existingCardId) {
|
|
@@ -551,6 +645,17 @@ export class StreamCardBuilder {
|
|
|
551
645
|
return Array.from(this.cards.values());
|
|
552
646
|
}
|
|
553
647
|
toolResult(toolUseId, content, isError) {
|
|
648
|
+
// Route to active subagent's nested tool call
|
|
649
|
+
const subagentCardId = this.nestedToolUseToSubagentCard.get(toolUseId);
|
|
650
|
+
if (subagentCardId) {
|
|
651
|
+
const subagentCard = this.cards.get(subagentCardId);
|
|
652
|
+
if (!subagentCard?.toolCalls)
|
|
653
|
+
return null;
|
|
654
|
+
const truncated = content.length > TOOL_RESULT_TRUNCATE_LENGTH;
|
|
655
|
+
const resultContent = truncated ? content.slice(0, TOOL_RESULT_TRUNCATE_LENGTH) + ' [truncated]' : content;
|
|
656
|
+
const toolCalls = subagentCard.toolCalls.map(tc => tc.id === toolUseId ? { ...tc, result: { content: resultContent, isError } } : tc);
|
|
657
|
+
return this.updateEvent(subagentCardId, { toolCalls });
|
|
658
|
+
}
|
|
554
659
|
const cardId = this.toolUseIdToCardId.get(toolUseId);
|
|
555
660
|
if (!cardId)
|
|
556
661
|
return null;
|
|
@@ -569,9 +674,19 @@ export class StreamCardBuilder {
|
|
|
569
674
|
return null;
|
|
570
675
|
return this.updateEvent(cardId, { answers });
|
|
571
676
|
}
|
|
572
|
-
|
|
677
|
+
/** Stash Agent tool_use input fields for later use when task_started arrives. */
|
|
678
|
+
stashAgentToolUseInput(toolUseId, input) {
|
|
679
|
+
this.agentToolUseStash.set(toolUseId, input);
|
|
680
|
+
}
|
|
681
|
+
subagentStart(description, agentId, toolUseId, extras) {
|
|
573
682
|
this.currentTextCardId = null;
|
|
574
683
|
const id = this.nextId();
|
|
684
|
+
const stashed = toolUseId ? this.agentToolUseStash.get(toolUseId) : undefined;
|
|
685
|
+
if (toolUseId)
|
|
686
|
+
this.agentToolUseStash.delete(toolUseId);
|
|
687
|
+
const subagentType = stashed?.subagentType ?? extras?.subagentType;
|
|
688
|
+
const requestedModel = stashed?.requestedModel ?? extras?.requestedModel;
|
|
689
|
+
const prompt = extras?.prompt ?? stashed?.prompt;
|
|
575
690
|
const card = {
|
|
576
691
|
type: 'subagent', id, timestamp: Date.now(),
|
|
577
692
|
description,
|
|
@@ -579,9 +694,12 @@ export class StreamCardBuilder {
|
|
|
579
694
|
agentId,
|
|
580
695
|
status: 'running',
|
|
581
696
|
toolUseCount: 0,
|
|
697
|
+
...(subagentType ? { subagentType } : {}),
|
|
698
|
+
...(requestedModel ? { requestedModel } : {}),
|
|
699
|
+
...(prompt ? { prompt } : {}),
|
|
582
700
|
};
|
|
583
701
|
this.agentIdToCardId.set(agentId, id);
|
|
584
|
-
|
|
702
|
+
this.activeSubagentCardId = id;
|
|
585
703
|
const afterCardId = toolUseId ? this.toolUseIdToCardId.get(toolUseId) : undefined;
|
|
586
704
|
return this.addEvent(card, afterCardId);
|
|
587
705
|
}
|
|
@@ -602,12 +720,34 @@ export class StreamCardBuilder {
|
|
|
602
720
|
?? (toolUseId ? this.agentIdToCardId.get(toolUseId) : undefined);
|
|
603
721
|
if (!cardId)
|
|
604
722
|
return null;
|
|
723
|
+
if (cardId === this.activeSubagentCardId)
|
|
724
|
+
this.activeSubagentCardId = null;
|
|
605
725
|
return this.updateEvent(cardId, { status, summary });
|
|
606
726
|
}
|
|
607
727
|
systemMessage(text, subtype) {
|
|
608
728
|
const card = { type: 'system', id: this.nextId(), timestamp: Date.now(), text, subtype };
|
|
609
729
|
return this.addEvent(card);
|
|
610
730
|
}
|
|
731
|
+
/** Live structured `turn_duration` card — mirrors the history builder so a
|
|
732
|
+
* reload renders identically. */
|
|
733
|
+
turnDuration(raw) {
|
|
734
|
+
return this.addEvent(turnDurationCard(raw, this.nextId()));
|
|
735
|
+
}
|
|
736
|
+
/** Live structured `stop_hook_summary` card — see history builder counterpart. */
|
|
737
|
+
stopHookSummary(raw) {
|
|
738
|
+
return this.addEvent(stopHookSummaryCard(raw, this.nextId()));
|
|
739
|
+
}
|
|
740
|
+
generatedImage(prompt, status, savedPath) {
|
|
741
|
+
const card = {
|
|
742
|
+
type: 'generated_image',
|
|
743
|
+
id: this.nextId(),
|
|
744
|
+
timestamp: Date.now(),
|
|
745
|
+
prompt,
|
|
746
|
+
status,
|
|
747
|
+
...(savedPath ? { savedPath } : {}),
|
|
748
|
+
};
|
|
749
|
+
return this.addEvent(card);
|
|
750
|
+
}
|
|
611
751
|
recoverySuggested(reason, action, label) {
|
|
612
752
|
const card = {
|
|
613
753
|
type: 'recovery_suggested',
|
|
@@ -623,6 +763,54 @@ export class StreamCardBuilder {
|
|
|
623
763
|
return this.systemMessage(`Error: ${text}`, 'error');
|
|
624
764
|
}
|
|
625
765
|
}
|
|
766
|
+
/** Read tool calls from a subagent's JSONL — used by buildCardsFromHistory to nest them. */
|
|
767
|
+
async function readSubagentToolCalls(jsonlPath) {
|
|
768
|
+
try {
|
|
769
|
+
const content = await readFile(jsonlPath, 'utf-8');
|
|
770
|
+
const messages = parseJSONLContent(content);
|
|
771
|
+
const resultMap = new Map();
|
|
772
|
+
const toolCalls = [];
|
|
773
|
+
for (const msg of messages) {
|
|
774
|
+
const rawMsg = msg.message;
|
|
775
|
+
if (!Array.isArray(rawMsg?.content))
|
|
776
|
+
continue;
|
|
777
|
+
if (msg.type === 'user') {
|
|
778
|
+
for (const block of rawMsg.content) {
|
|
779
|
+
if (block.type === 'tool_result' && block.tool_use_id) {
|
|
780
|
+
const text = extractToolResultText(block.content);
|
|
781
|
+
resultMap.set(block.tool_use_id, { content: text, isError: !!block.is_error });
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
else if (msg.type === 'assistant') {
|
|
786
|
+
for (const block of rawMsg.content) {
|
|
787
|
+
if (block.type === 'tool_use' && block.id && block.name !== 'Agent') {
|
|
788
|
+
toolCalls.push({
|
|
789
|
+
id: block.id,
|
|
790
|
+
toolName: block.name,
|
|
791
|
+
toolInput: typeof block.input === 'object' && block.input ? block.input : {},
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
// Attach results and apply truncation
|
|
798
|
+
for (const tc of toolCalls) {
|
|
799
|
+
const r = resultMap.get(tc.id);
|
|
800
|
+
if (r) {
|
|
801
|
+
const truncated = r.content.length > TOOL_RESULT_TRUNCATE_LENGTH;
|
|
802
|
+
tc.result = {
|
|
803
|
+
content: truncated ? r.content.slice(0, TOOL_RESULT_TRUNCATE_LENGTH) + ' [truncated]' : r.content,
|
|
804
|
+
isError: r.isError,
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
return toolCalls;
|
|
809
|
+
}
|
|
810
|
+
catch {
|
|
811
|
+
return [];
|
|
812
|
+
}
|
|
813
|
+
}
|
|
626
814
|
// ============================================================================
|
|
627
815
|
// buildCardsFromHistory — convert JSONL history into Card[]
|
|
628
816
|
// ============================================================================
|
|
@@ -682,7 +870,52 @@ export async function buildCardsFromHistory(sessionId, cwd, offset = 0, limit =
|
|
|
682
870
|
// The Claude CLI stores this as `toolUseResult` at the message envelope
|
|
683
871
|
// level, separate from the human-readable tool_result.content string.
|
|
684
872
|
const askUserResults = new Map();
|
|
873
|
+
const taskStarts = new Map();
|
|
874
|
+
const taskProgress = new Map();
|
|
875
|
+
const taskEnds = new Map();
|
|
876
|
+
const taskKey = (agentId, toolUseId) => {
|
|
877
|
+
if (typeof toolUseId === 'string' && toolUseId.length > 0)
|
|
878
|
+
return toolUseId;
|
|
879
|
+
if (typeof agentId === 'string' && agentId.length > 0)
|
|
880
|
+
return agentId;
|
|
881
|
+
return null;
|
|
882
|
+
};
|
|
685
883
|
for (const msg of allMessages) {
|
|
884
|
+
if (msg.type === 'system') {
|
|
885
|
+
const subtype = msg.subtype;
|
|
886
|
+
const key = taskKey(msg.task_id, msg.tool_use_id);
|
|
887
|
+
if (key && subtype === 'task_started') {
|
|
888
|
+
taskStarts.set(key, {
|
|
889
|
+
description: typeof msg.description === 'string' ? msg.description : '',
|
|
890
|
+
agentId: typeof msg.task_id === 'string' ? msg.task_id : key,
|
|
891
|
+
toolUseId: typeof msg.tool_use_id === 'string' ? msg.tool_use_id : key,
|
|
892
|
+
prompt: typeof msg.prompt === 'string' ? msg.prompt : undefined,
|
|
893
|
+
subagentType: typeof msg.subagent_type === 'string'
|
|
894
|
+
? msg.subagent_type
|
|
895
|
+
: typeof msg.subagentType === 'string'
|
|
896
|
+
? msg.subagentType
|
|
897
|
+
: undefined,
|
|
898
|
+
requestedModel: typeof msg.model === 'string'
|
|
899
|
+
? msg.model
|
|
900
|
+
: typeof msg.requested_model === 'string'
|
|
901
|
+
? msg.requested_model
|
|
902
|
+
: undefined,
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
else if (key && subtype === 'task_progress') {
|
|
906
|
+
taskProgress.set(key, {
|
|
907
|
+
toolUseCount: typeof msg.usage?.tool_uses === 'number' ? msg.usage.tool_uses : undefined,
|
|
908
|
+
lastToolName: typeof msg.last_tool_name === 'string' ? msg.last_tool_name : undefined,
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
else if (key && subtype === 'task_notification') {
|
|
912
|
+
const status = msg.status;
|
|
913
|
+
taskEnds.set(key, {
|
|
914
|
+
status: status === 'failed' || status === 'stopped' ? status : 'completed',
|
|
915
|
+
summary: typeof msg.summary === 'string' ? msg.summary : undefined,
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
}
|
|
686
919
|
const rawMsg = msg.message;
|
|
687
920
|
const toolUseResult = msg.toolUseResult;
|
|
688
921
|
if (!Array.isArray(rawMsg?.content))
|
|
@@ -707,24 +940,32 @@ export async function buildCardsFromHistory(sessionId, cwd, offset = 0, limit =
|
|
|
707
940
|
// System messages (task_started) are NOT reliably present in the JSONL.
|
|
708
941
|
// Instead, read subagent meta.json files and match by description to the
|
|
709
942
|
// Agent tool_use input.description.
|
|
710
|
-
// Build description → toolUseId map
|
|
943
|
+
// Build description → toolUseId map and capture Agent tool_use input data
|
|
711
944
|
const descToToolUseId = new Map();
|
|
945
|
+
const agentInputByToolUseId = new Map();
|
|
712
946
|
for (const msg of allMessages) {
|
|
713
947
|
const rawMsg = msg.message;
|
|
714
948
|
if (!Array.isArray(rawMsg?.content))
|
|
715
949
|
continue;
|
|
716
950
|
for (const block of rawMsg.content) {
|
|
717
951
|
if (block.type === 'tool_use' && block.name === 'Agent' && block.id) {
|
|
718
|
-
const
|
|
952
|
+
const input = block.input;
|
|
953
|
+
const desc = input?.description ?? '';
|
|
719
954
|
if (desc)
|
|
720
955
|
descToToolUseId.set(desc, block.id);
|
|
956
|
+
agentInputByToolUseId.set(block.id, {
|
|
957
|
+
prompt: typeof input?.prompt === 'string' ? input.prompt : undefined,
|
|
958
|
+
subagentType: typeof input?.subagent_type === 'string' ? input.subagent_type : undefined,
|
|
959
|
+
requestedModel: typeof input?.model === 'string' ? input.model : undefined,
|
|
960
|
+
});
|
|
721
961
|
}
|
|
722
962
|
}
|
|
723
963
|
}
|
|
964
|
+
const subagentsDir = join(claudeProjectDir(cwd), sessionId, 'subagents');
|
|
724
965
|
const toolUseIdToAgentId = new Map();
|
|
966
|
+
const subagentToolCallsByToolUseId = new Map();
|
|
725
967
|
try {
|
|
726
968
|
const agentIds = await listSubagentIdsFromDisk(sessionId, cwd);
|
|
727
|
-
const subagentsDir = join(claudeProjectDir(cwd), sessionId, 'subagents');
|
|
728
969
|
for (const agentId of agentIds) {
|
|
729
970
|
try {
|
|
730
971
|
const meta = JSON.parse(await readFile(join(subagentsDir, `${agentId}.meta.json`), 'utf-8'));
|
|
@@ -740,6 +981,12 @@ export async function buildCardsFromHistory(sessionId, cwd, offset = 0, limit =
|
|
|
740
981
|
toolUseIdToAgentId.set(toolUseId, toolUseId);
|
|
741
982
|
}
|
|
742
983
|
}
|
|
984
|
+
// Read each subagent's JSONL to extract nested tool calls
|
|
985
|
+
for (const [toolUseId, agentId] of toolUseIdToAgentId) {
|
|
986
|
+
const toolCalls = await readSubagentToolCalls(join(subagentsDir, `${agentId}.jsonl`));
|
|
987
|
+
if (toolCalls.length > 0)
|
|
988
|
+
subagentToolCallsByToolUseId.set(toolUseId, toolCalls);
|
|
989
|
+
}
|
|
743
990
|
}
|
|
744
991
|
catch {
|
|
745
992
|
// No subagents or SDK error — fallback: agentId = toolUseId
|
|
@@ -765,12 +1012,54 @@ export async function buildCardsFromHistory(sessionId, cwd, offset = 0, limit =
|
|
|
765
1012
|
let seq = Math.max(0, total - offset - limit);
|
|
766
1013
|
const nextId = () => `${sessionId}:h:${++seq}`;
|
|
767
1014
|
const cards = [];
|
|
1015
|
+
const emittedSubagentKeys = new Set();
|
|
1016
|
+
const slicedAgentToolUseIds = new Set();
|
|
1017
|
+
for (const msg of sliced) {
|
|
1018
|
+
const rawMsg = msg.message;
|
|
1019
|
+
if (!Array.isArray(rawMsg?.content))
|
|
1020
|
+
continue;
|
|
1021
|
+
for (const block of rawMsg.content) {
|
|
1022
|
+
if (block.type === 'tool_use' && block.name === 'Agent' && block.id) {
|
|
1023
|
+
slicedAgentToolUseIds.add(block.id);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
const subagentCardFromTask = (task) => {
|
|
1028
|
+
const key = task.toolUseId || task.agentId;
|
|
1029
|
+
const end = taskEnds.get(key);
|
|
1030
|
+
const progress = taskProgress.get(key);
|
|
1031
|
+
return {
|
|
1032
|
+
type: 'subagent',
|
|
1033
|
+
id: nextId(),
|
|
1034
|
+
timestamp: Date.now(),
|
|
1035
|
+
description: task.description,
|
|
1036
|
+
toolUseId: task.toolUseId,
|
|
1037
|
+
agentId: task.agentId,
|
|
1038
|
+
status: end?.status ?? 'running',
|
|
1039
|
+
...(end?.summary ? { summary: end.summary } : {}),
|
|
1040
|
+
toolUseCount: progress?.toolUseCount ?? 0,
|
|
1041
|
+
...(progress?.lastToolName ? { lastToolName: progress.lastToolName } : {}),
|
|
1042
|
+
...(task.prompt ? { prompt: task.prompt } : {}),
|
|
1043
|
+
...(task.subagentType ? { subagentType: task.subagentType } : {}),
|
|
1044
|
+
...(task.requestedModel ? { requestedModel: task.requestedModel } : {}),
|
|
1045
|
+
};
|
|
1046
|
+
};
|
|
768
1047
|
for (const msg of sliced) {
|
|
769
1048
|
// ── System messages ──
|
|
770
1049
|
if (msg.type === 'system') {
|
|
771
1050
|
const subtype = msg.subtype;
|
|
772
|
-
|
|
773
|
-
if (subtype === 'task_started'
|
|
1051
|
+
const key = taskKey(msg.task_id, msg.tool_use_id);
|
|
1052
|
+
if (subtype === 'task_started' && key) {
|
|
1053
|
+
const task = taskStarts.get(key);
|
|
1054
|
+
if (task && !slicedAgentToolUseIds.has(task.toolUseId) && !emittedSubagentKeys.has(key)) {
|
|
1055
|
+
cards.push(subagentCardFromTask(task));
|
|
1056
|
+
emittedSubagentKeys.add(key);
|
|
1057
|
+
}
|
|
1058
|
+
continue;
|
|
1059
|
+
}
|
|
1060
|
+
// Progress/end task events update the task_started card; they do not
|
|
1061
|
+
// render as standalone history cards.
|
|
1062
|
+
if (subtype === 'task_progress' || subtype === 'task_notification')
|
|
774
1063
|
continue;
|
|
775
1064
|
// Skip init and status messages (not useful in history)
|
|
776
1065
|
if (subtype === 'init' || subtype === 'status' || subtype === 'session_state_changed')
|
|
@@ -778,6 +1067,12 @@ export async function buildCardsFromHistory(sessionId, cwd, offset = 0, limit =
|
|
|
778
1067
|
if (subtype === 'compact_boundary') {
|
|
779
1068
|
cards.push({ type: 'system', id: nextId(), timestamp: Date.now(), text: 'Context compacted', subtype: 'compacted' });
|
|
780
1069
|
}
|
|
1070
|
+
else if (subtype === 'turn_duration') {
|
|
1071
|
+
cards.push(turnDurationCard(msg, nextId()));
|
|
1072
|
+
}
|
|
1073
|
+
else if (subtype === 'stop_hook_summary') {
|
|
1074
|
+
cards.push(stopHookSummaryCard(msg, nextId()));
|
|
1075
|
+
}
|
|
781
1076
|
else {
|
|
782
1077
|
// Unknown system subtype — show as info
|
|
783
1078
|
cards.push({ type: 'system', id: nextId(), timestamp: Date.now(), text: subtype ?? 'System event', subtype: 'info' });
|
|
@@ -901,12 +1196,17 @@ export async function buildCardsFromHistory(sessionId, cwd, offset = 0, limit =
|
|
|
901
1196
|
break;
|
|
902
1197
|
case 'tool_use': {
|
|
903
1198
|
if (agentToolUseIds.has(block.id)) {
|
|
904
|
-
// Agent tool_use →
|
|
1199
|
+
// Agent tool_use → SubagentCard
|
|
905
1200
|
const agentId = toolUseIdToAgentId.get(block.id) ?? block.id;
|
|
906
1201
|
const result = toolResults.get(block.id);
|
|
907
1202
|
const description = block.input?.description ?? '';
|
|
908
1203
|
const summary = result ? result.content.slice(0, 200) : undefined;
|
|
909
|
-
|
|
1204
|
+
const agentInput = agentInputByToolUseId.get(block.id);
|
|
1205
|
+
const taskStart = taskStarts.get(block.id);
|
|
1206
|
+
const progress = taskProgress.get(block.id);
|
|
1207
|
+
const end = taskEnds.get(block.id);
|
|
1208
|
+
const prompt = agentInput?.prompt ?? taskStart?.prompt;
|
|
1209
|
+
const nestedToolCalls = subagentToolCallsByToolUseId.get(block.id);
|
|
910
1210
|
const subagentCard = {
|
|
911
1211
|
type: 'subagent',
|
|
912
1212
|
id: nextId(),
|
|
@@ -914,11 +1214,17 @@ export async function buildCardsFromHistory(sessionId, cwd, offset = 0, limit =
|
|
|
914
1214
|
description,
|
|
915
1215
|
toolUseId: block.id,
|
|
916
1216
|
agentId,
|
|
917
|
-
status: result ? 'completed' : 'running',
|
|
918
|
-
summary,
|
|
919
|
-
toolUseCount: 0,
|
|
1217
|
+
status: end?.status ?? (result ? 'completed' : 'running'),
|
|
1218
|
+
summary: end?.summary ?? summary,
|
|
1219
|
+
toolUseCount: progress?.toolUseCount ?? nestedToolCalls?.length ?? 0,
|
|
1220
|
+
...(progress?.lastToolName ? { lastToolName: progress.lastToolName } : {}),
|
|
1221
|
+
...(agentInput?.subagentType ? { subagentType: agentInput.subagentType } : {}),
|
|
1222
|
+
...(agentInput?.requestedModel ? { requestedModel: agentInput.requestedModel } : {}),
|
|
1223
|
+
...(prompt ? { prompt } : {}),
|
|
1224
|
+
...(nestedToolCalls?.length ? { toolCalls: nestedToolCalls } : {}),
|
|
920
1225
|
};
|
|
921
1226
|
cards.push(subagentCard);
|
|
1227
|
+
emittedSubagentKeys.add(block.id);
|
|
922
1228
|
}
|
|
923
1229
|
else {
|
|
924
1230
|
// Normal tool call
|