botholomew 0.9.8 → 0.9.9
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/package.json +1 -1
- package/src/chat/agent.ts +13 -0
- package/src/tui/App.tsx +11 -0
- package/src/tui/components/MessageList.tsx +12 -0
- package/src/worker/fake-llm.ts +14 -1
package/package.json
CHANGED
package/src/chat/agent.ts
CHANGED
|
@@ -160,6 +160,7 @@ export interface ToolEndMeta {
|
|
|
160
160
|
|
|
161
161
|
export interface ChatTurnCallbacks {
|
|
162
162
|
onToken: (text: string) => void;
|
|
163
|
+
onToolPreparing?: (id: string, name: string) => void;
|
|
163
164
|
onToolStart: (id: string, name: string, input: string) => void;
|
|
164
165
|
onToolEnd: (
|
|
165
166
|
id: string,
|
|
@@ -251,6 +252,18 @@ export async function runChatTurn(input: {
|
|
|
251
252
|
callbacks.onToken(text);
|
|
252
253
|
});
|
|
253
254
|
|
|
255
|
+
stream.on("streamEvent", (event) => {
|
|
256
|
+
if (
|
|
257
|
+
event.type === "content_block_start" &&
|
|
258
|
+
event.content_block.type === "tool_use"
|
|
259
|
+
) {
|
|
260
|
+
callbacks.onToolPreparing?.(
|
|
261
|
+
event.content_block.id,
|
|
262
|
+
event.content_block.name,
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
|
|
254
267
|
stream.on("contentBlock", (block) => {
|
|
255
268
|
if (block.type === "tool_use") {
|
|
256
269
|
earlyReportedToolIds.add(block.id);
|
package/src/tui/App.tsx
CHANGED
|
@@ -133,6 +133,10 @@ export function App({
|
|
|
133
133
|
const [isLoading, setIsLoading] = useState(false);
|
|
134
134
|
const [streamingText, setStreamingText] = useState("");
|
|
135
135
|
const [activeToolCalls, setActiveToolCalls] = useState<ToolCallData[]>([]);
|
|
136
|
+
const [preparingTool, setPreparingTool] = useState<{
|
|
137
|
+
id: string;
|
|
138
|
+
name: string;
|
|
139
|
+
} | null>(null);
|
|
136
140
|
const [ready, setReady] = useState(false);
|
|
137
141
|
const skipSplash = !!(resumeThreadId || initialPrompt);
|
|
138
142
|
const [splashDone, setSplashDone] = useState(skipSplash);
|
|
@@ -329,6 +333,7 @@ export function App({
|
|
|
329
333
|
setIsLoading(true);
|
|
330
334
|
setStreamingText("");
|
|
331
335
|
setActiveToolCalls([]);
|
|
336
|
+
setPreparingTool(null);
|
|
332
337
|
|
|
333
338
|
const userMsg: ChatMessage = {
|
|
334
339
|
id: msgId(),
|
|
@@ -370,6 +375,9 @@ export function App({
|
|
|
370
375
|
lastStreamFlush = now;
|
|
371
376
|
}
|
|
372
377
|
},
|
|
378
|
+
onToolPreparing: (id, name) => {
|
|
379
|
+
setPreparingTool({ id, name });
|
|
380
|
+
},
|
|
373
381
|
onToolStart: (id, name, input) => {
|
|
374
382
|
if (currentText) {
|
|
375
383
|
finalizeSegment();
|
|
@@ -383,6 +391,7 @@ export function App({
|
|
|
383
391
|
};
|
|
384
392
|
pendingToolCalls.push(tc);
|
|
385
393
|
setActiveToolCalls([...pendingToolCalls]);
|
|
394
|
+
setPreparingTool(null);
|
|
386
395
|
},
|
|
387
396
|
onToolEnd: (id, _name, output, isError, meta) => {
|
|
388
397
|
const tc = pendingToolCalls.find((t) => t.id === id);
|
|
@@ -410,6 +419,7 @@ export function App({
|
|
|
410
419
|
} finally {
|
|
411
420
|
setStreamingText("");
|
|
412
421
|
setActiveToolCalls([]);
|
|
422
|
+
setPreparingTool(null);
|
|
413
423
|
}
|
|
414
424
|
}
|
|
415
425
|
|
|
@@ -700,6 +710,7 @@ export function App({
|
|
|
700
710
|
streamingText={streamingText}
|
|
701
711
|
isLoading={isLoading}
|
|
702
712
|
activeToolCalls={activeToolCalls}
|
|
713
|
+
preparingTool={preparingTool}
|
|
703
714
|
/>
|
|
704
715
|
</Box>
|
|
705
716
|
<Box
|
|
@@ -17,6 +17,7 @@ interface MessageListProps {
|
|
|
17
17
|
streamingText: string;
|
|
18
18
|
isLoading: boolean;
|
|
19
19
|
activeToolCalls: ToolCallData[];
|
|
20
|
+
preparingTool: { id: string; name: string } | null;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
function formatTime(date: Date): string {
|
|
@@ -127,6 +128,7 @@ export function MessageList({
|
|
|
127
128
|
streamingText,
|
|
128
129
|
isLoading,
|
|
129
130
|
activeToolCalls,
|
|
131
|
+
preparingTool,
|
|
130
132
|
}: MessageListProps) {
|
|
131
133
|
return (
|
|
132
134
|
<>
|
|
@@ -160,7 +162,17 @@ export function MessageList({
|
|
|
160
162
|
</Box>
|
|
161
163
|
)}
|
|
162
164
|
|
|
165
|
+
{preparingTool && (
|
|
166
|
+
<Box marginTop={1}>
|
|
167
|
+
<Text color={theme.accent}>
|
|
168
|
+
<Spinner type="dots" />
|
|
169
|
+
</Text>
|
|
170
|
+
<Text dimColor> Preparing tool call: {preparingTool.name}...</Text>
|
|
171
|
+
</Box>
|
|
172
|
+
)}
|
|
173
|
+
|
|
163
174
|
{isLoading &&
|
|
175
|
+
!preparingTool &&
|
|
164
176
|
!streamingText &&
|
|
165
177
|
(activeToolCalls.length === 0 ||
|
|
166
178
|
activeToolCalls.every((tc) => !tc.running)) && (
|
package/src/worker/fake-llm.ts
CHANGED
|
@@ -144,9 +144,22 @@ class FakeMessageStream extends EventEmitter {
|
|
|
144
144
|
if (delay > 0) await new Promise((r) => setTimeout(r, delay));
|
|
145
145
|
}
|
|
146
146
|
const final = buildFinalMessage(text, this.turn.toolCalls);
|
|
147
|
+
let blockIndex = text ? 1 : 0;
|
|
147
148
|
for (const block of final.content) {
|
|
148
149
|
if ((block as { type: string }).type === "tool_use") {
|
|
149
|
-
|
|
150
|
+
const toolUse = block as ToolUseBlock;
|
|
151
|
+
this.emit("streamEvent", {
|
|
152
|
+
type: "content_block_start",
|
|
153
|
+
index: blockIndex,
|
|
154
|
+
content_block: {
|
|
155
|
+
type: "tool_use",
|
|
156
|
+
id: toolUse.id,
|
|
157
|
+
name: toolUse.name,
|
|
158
|
+
input: {},
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
this.emit("contentBlock", toolUse);
|
|
162
|
+
blockIndex++;
|
|
150
163
|
}
|
|
151
164
|
}
|
|
152
165
|
this.resolveFinal(final);
|